Compare commits

..

24 Commits
v042 ... v059

Author SHA1 Message Date
byuu
97a3a28d86 Update to bsnes v059 release.
**Known issues:**
- button menus do not show up with Windows Vista/7 theme
- snesreader's multi-file archive dialog box doesn't redraw itself on
Windows when you choose different games

Windows Qt is buggy as always. Nothing we can do but keep waiting. I'm
also going to hold off on including pixel shaders until Direct3D PS
support is in. It's just going to annoy the 98% of users who can't use
them if I include them now. Yes, Windows OpenGL support is that bad.

Anyway, from v058 wip10, the following changes were made:
- cheat code editor grays out the slot#s when they are empty. I can't
put "Empty" in the text boxes for various reasons.
- added "Clear Selected" button and multi-selection support to cheat
editor. This is meant to quickly erase all slots.
- settings and tools windows start at 600x360 when bsnes.cfg is not
found / empty
- fixed the emulationSpeed section to start with input. instead of
config.
- open-folder concept requires the folders to end in .sfc to work now,
once again doesn't care what the ROM inside is named
(this is meant to mimic OS X .app folders)
- 21fx API extended to map to $2200, $2201 for now; mostly as a test
for A-bus access (21fx->VRAM DMA, etc)
(old $21fx registers remain for now)

I intend to release this on Saturday as-is even if a few small bugs
are reported. But if there's something major we can make another RC
build.
2010-01-07 13:07:56 +00:00
byuu
6ec765f2c4 Update to bsnes v058 release.
We've tested the latest release on at least a dozen computers now, all seems to be in order for a release.
Changelog:
    - added 21fx support (more on this later)
    - added movie recording and playback support
    - added rewind support (enable under Settings->Configuration->Advanced, use backspace key to rewind)
    - added speedup (fast forward) and slowdown key bindings
    - audio no longer stutters on Windows when moving or resizing the main window
    - co-processors can now specify their own clock rates instead of sharing the S-CPU clock rate
    - Super Game Boy 2 now runs at the correct hardware speed, and not 2.4% faster like the Super Game Boy 1 does
    - added Vsync support to the Windows OpenGL driver (Intel graphics drivers do not support this option, because their engineers are lazy)
    - OpenGL driver no longer re-initializes when changing video synchronization, helps pixel shaders
    - refactored user interface compilation; now split into several object files, auto-generated MOC files placed under src/obj/
    - worked around a bug in the PulseAudio sound server that was causing the ALSA output driver to lock up [BearOso]
    - rewrote and simplified the save state manager, it is no longer a part of the core
    - S-DD1 and SPC7110 can now access up to 256MB via their MMCs
    - re-added background and OAM layer toggling under the tools dialog
    - added config file options to adjust emulation speed levels (config.system.speed*)
    - added snesreader, snesfilter and supergameboy support to the OS X port
    - added a really neat pixel shader that can perform point scaling to non-even multiples, eg it looks great even with aspect correction [Fes]
    - upgraded to Qt 4.6.0 official
Debugger changelog:
    - added memory export and import to the memory editor
    - added bus usage analyzer: logs opcodes, memory reads, memory writes and M/X states to usage.bin file
    - added disassembler that can trace both forward and backward from the current execution address
    - extended read/write breakpoints to the S-SMP
    - re-added trace masking option
Errata: there is one known bug in Qt 4.6.0 that affects the Windows port: menus attached to buttons show up as invisible on Windows Vista and above. I only use this on the file load dialog options button, and only to toggle the information pane on and off. Given that this is less severe than the bugs in the beta versions, I've upgraded anyway. I'll submit a bug report to the Qt team for this shortly. Also, my sincerest thanks to Bradley Hughes from the Qt development team for quickly fixing this show-stopper bug that greatly affected performance in bsnes v056.
2009-12-09 13:34:03 +00:00
byuu
54c7b4692d Update to bsnes v057 release.
I'm really sorry about this, but a major issue snuck into v056. It was caused by a bug in the newly released Qt 4.6.0 RC1. Whenever one moved the mouse cursor over the main window in the Windows port, the frame rate was immediately cut in half, which effectively ruined Mouse, Super Scope and Justifier support. As for how this could happen, well ... I'm ... really at a loss for words about this.
This release does not change the source code at all except to increment the version number, and it is built against Qt 4.6.0 beta 1 instead of 4.6.0 release candidate 1 as v055 was.
I will file an official bug complaint and post a link to it here during next week. Again, my apologies for any inconvenience. I incorrectly assumed it would be safe to update to RC1, and didn't spot the bug in time.
2009-11-23 13:24:03 +00:00
byuu
66067f0015 Update to bsnes v056 release.
This release adds a lot of new user interface features, and polishes Super Game Boy support.
Note that many pixel shaders need to be coded specifically for bsnes, eg ones designed for Pete's OpenGL2 plugin will not work. I will maintain a pixelshaders archive on the bsnes download page with a collection of working shaders. Right now, there are three: HDR TV, Scale2x and HQ2x; written by guest(r) and Pete, and ported by myself.
Changelog:
    - lowered Game Boy audio volume so that it matches SNES audio volume
    - fixed Super Game Boy multi-player support
    - fixed Super Game Boy swapped player bug
    - compressed Game Boy cartridges can now be loaded
    - added save state support for Super Game Boy games
    - blocked illegal Super Game Boy packets, fixes Zelda DX, Akumajou Dracula, etc palette issues
    - main window once again shrinks on size changes
    - joypads can now control the file loading window (support is very rudimentary)
    - cleaned up video and audio sliders, increased audio input frequency range for 59hz monitors
    - rewrote all of the input capture system from scratch
    - added dozens of additional GUI hotkey bindings to resize the main window, control synchronization, control speed, etc
    - it is now possible to map keyboard modifiers (shift, control, alt, super) to any input or hotkey; eg alt+enter = fullscreen
    - merged all input capture windows into the main settings panel
    - added turbo button support; hold down turbo buttons to send a 30hz input pulse
    - added asciiPad controller emulation; contains off/turbo/auto fire toggles and slow-motion mode
    - asciiPad support allows for quick switching between keyboard and gamepad input
    - merged scanline filter into the user interface (under Video Settings) to allow it to work on all filters; including the NTSC filter
    - killed off an evil QString <> string intermediary class called utf8; string class can convert to and from QString directly now
    - added fast BS-X, Sufami Turbo and Game Boy cartridge loading: use the filter list under "Load Cartridge" to bypass the BIOS selection screen
    - added pixel shader support to the OpenGL driver on Windows and Linux; note that it only really works well on Linux at the moment
    - added proper Vsync support to the OpenGL driver on Windows and Linux using GL extensions; again this really only works well on Linux
    - added unique path memory for shaders, folders, cartridges, BS-X, Sufami Turbo and Game Boy images
    - upgraded to Qt 4.6.0 release candidate 1; fixes an issue with the first checkbox in lists not updating when clicked
2009-11-22 14:48:58 +00:00
byuu
4c66de6f27 Update to bsnes v055 release.
Happy Halloween, this release adds full Super Game Boy support ... but is it a trick, or a treat? ;) ::cough::, lameness aside ...
The Game Boy emulation core is courtesy of gambatte, and excellent, accuracy-focused, open source, and lightning fast Game Boy Color emulator. Now I know what you're thinking, using a Game Boy Color emulator with the Super Game Boy? The truth is, gambatte was just such an amazingly perfect fit that nothing else compared. I fully believe that even as a CGB emulator, gambatte will do a better job than any pure DMG emulator could.
The emulation of the ICD2 chip (aka the Super Game Boy) was fully reverse engineered by myself. Eventually I'll get an updated document put up explaining how it works.
The next question might be, "why emulate the Super Game Boy when existing Game Boy emulators do?"; well, they can only simulate part of the SGB. Features such as custom SNES sound effects, hand-drawn borders, multi-tap support and custom SNES code execution can only be accomplished by a true SNES emulator. Space Invaders is perhaps the most impressive demonstration, as it contains an entire SNES game embedded inside the Game Boy cartridge.
bsnes' SGB emulation supports virtually every command, full sound mixing from both the SNES and Game Boy sides, both BIOS revisions, etc. The only thing that is not fully functional yet is the multi-player support, but it should be in due time. Save state support is also planned for a later date.
Changelog:
    - added Super Game Boy emulation (thanks to gambatte for the Game Boy core)
    - extended hybrid scanline/cycle PPU renderer to support Mode7 register caching; fixes scanline flickering on NHL '94 title screen
    - all windows (other than the main window) can be closed with the escape key now
    - file dialog path selection now accepts typed paths; can be used to access hidden directories and network shares
    - file dialog's game information panel can now be disabled
    - fixed a crashing issue when the file dialog was given an invalid path
    - fixed screenshot capture save location
    - added screenshot capture option to tools menu
    - state manager now auto-closes when loading a state; it can be reopened quickly with F3
    - fixed GZip archive loading
    - fixed NTSC off-by-one filter bug on hires screens
    - extended Scale2x, LQ2x and HQ2x to properly filter hires screens
    - added Pixellate2x filter
2009-11-01 14:30:51 +00:00
byuu
6a17b5ed4f Update to bsnes v054 release.
After a half-dozen hours of installing and compiling various combinations of MinGW and Qt, I've finally found a combination that once again allows for profile-guided optimizations: MinGW GCC 4.3.3 and Qt 4.6.0-beta 1. Though Qt 4.4 still has broken PGO, the latest Qt beta no longer has the process freeze issue upon termination.
This release is essentially the same as v053, but it's now at least as fast as v052 was, and ~10% faster than v053, which lacked profiling.
I did add in two quick changes, however: first, when starting in fullscreen mode, the video output size was being incorrectly set to the windowed size; second, by requiring save states to match the CRC32 of games, it made debugging with them impossible, so I've turned off the CRC32 matching.
2009-10-19 16:58:29 +00:00
byuu
8135dfdac9 Update to bsnes v053 release.
This release greatly polishes the user interface, adds a new cheat code search utility, adds the snesfilter library, and adds Qt-based GUI support to both snesfilter and snesreader. snesfilter gains 2xSaI, Super 2xSaI and Super Eagle support, plus full configuration for both the NTSC and scanline filters; and snesreader gains support support for multi-file ROM archives (eg GoodMerge sets.)
Statically linking Qt to bsnes, snesfilter and snesreader would be too prohibitive size-wise (~10MB or so.) I have to link dynamically so that all three can share the same Qt runtime, which gets all of bsnes and its modules to ~1MB (including the debugger build); and Qt itself to about ~2.5MB.
However, there is some bad news. There's a serious bug in MinGW 4.4+, where it is not generating profile-guided input files (*.gcno files.) There is also a serious bug in Qt 4.5.2/Windows when using dynamic linking: the library is hanging indefinitely, forcing me to manually terminate the process upon exit. This prevents the creation of profile-guided output files (*.gcda files.) It would be tough enough to work around one, but facing both of these issues at once is too much.
I'm afraid I have no choice but to disable profile-guided optimizations until these issues can be addressed. I did not know about these bugs until trying to build the official v053 release, so it's too late to revert to an all-in-one binary now. And I'm simply not willing to stop releasing new builds because of bugs in third-party software. As soon as I can work around this, I'll post a new optimized binary. In the mean time, despite the fact that this release is actually more optimized, please understand that the Windows binary will run approximately ~10% slower than previous releases. I recommend keeping v052 for now if you need the performance. Linux and OS X users are unaffected.
Changelog:
    - save RAM is initialized to 0xff again to work around Ken Griffey Jr Baseball issue
    - libco adds assembly-optimized targets for Win64 and PPC-ELF [the latter courtesy of Kernigh]
    - libco/x86 and libco/amd64 use pre-assembled blocks now, obviates need for custom compilation flags
    - added a new cheat code search utility to the tools menu
    - separated filters from main bsnes binary to libsnesfilter / snesfilter.dll
    - added 2xSaI, Super 2xSaI and Super Eagle filters [kode54]
    - added full configuration settings for NTSC and scanline filters (12+ new options)
    - further optimized HQ2x filter [blargg]
    - added Vsync support to the Mac OS X OpenGL driver
    - added folder creation button to custom file load dialog
    - fixed a few oddities with loading of "game folders" (see older news for an explanation on what this is)
    - updated to blargg's file_extractor v1.0.0
    - added full support for multi-file archives (eg GoodMerge sets)
    - split multi-cart loading again (BS-X, Sufami Turbo, etc) as required for multi-file support
    - cleaned up handling of file placement detection for save files (.srm, .cht, etc)
    - file load dialog now remembers your previous folder path across runs even without a custom games folder assigned
    - windows now save their exact positioning and size across runs, they no longer forcibly center
    - menus now have radio button and check box icons where appropriate
    - debugger's hex editor now has a working scrollbar widget
    - added resize splitter to settings and tools windows
    - worked around Qt style sheet bug where subclassed widgets were not properly applying style properties
2009-10-18 17:33:04 +00:00
byuu
a0000c7846 Update to bsnes v052 release.
This is a maintenance release, which fixes a few important bugs. It also adds some graphical icons to soften the user interface. Note that if you have set any custom paths with v051, you'll need to set them again for the fix to work. As always, my apologies for releasing two versions so close together. I felt the bugs were important enough to warrant it.
Changelog:
    - fixed loading of files and folders containing non-ANSI characters (Chinese, Japanese, etc)
    - fixed a slight lag on startup due to the new file browser
    - fixed path selection setting, screenshots will now be saved to the correct directory
    - hid memory editor scrollbar since it does not work yet
    - disabled window positioning on Linux due to bugs in the Compiz compositor
    - added icons from the Tango icon library to the menus and panels
2009-09-29 12:25:41 +00:00
byuu
b6a85353bf Update to bsnes v051 release.
Starting with this release, I wish to take bsnes in a new direction. It has always excelled in accuracy, as the only SNES emulator to offer a full 100% compatibility rate with all known commercial software. But over the years, it has also gained an impressive array of features and enhancements not found anywhere else. It is also the only actively developed SNES emulator with rapid, periodic releases. Its only achilles heel is the steep system requirements, which is quickly being overcome by aggressive new optimizations and steadily-increasing hardware speeds.
In an effort to make bsnes even more accessible to everyone, starting with this release, bsnes is now fully open source software, licensed under the terms of the GNU General Public License. I would like to work toward positioning bsnes as a truly general use emulator, and would welcome any help with this.
Specifically, I am looking for an interested Debian maintainer to package bsnes for Linux users; as well as for anyone interested in helping to optimize and improve bsnes as a whole. It also seems that many still do not know about bsnes, I'd appreciate advice and help on spreading the word. Please leave a message on my forum if you are interested.
I would also welcome and support any forks that target specific areas: a speed-oriented version, a tool-assisted speedrun version, netplay bindings, and so on. As part of this targeting, I've also released a custom debugger-enabled version, which trades a bit of speed in turn for best-in-class debugging capabilities.
Please check back here over the following few days, I'll be writing up documentation explaining all of the various unique features of bsnes, as well as detailed compilation instructions for programmers.
Changelog:
    - corrected a small bug in HDMA processing; fixes College Football '97 flickering
    - corrected ROMBR and PBR SuperFX register masking; fixes Voxel demo [MooglyGuy]
    - DSP-4 driver AI bug fixed [Jonas Quinn]
    - added save state support to the S-DD1, S-RTC, DSP-1, DSP-2 and ST-0010 co-processors
    - fixed a freeze issue when the S-SMP encounters STOP and SLEEP opcodes
    - Cx4 save states no longer need floating-point values, and are thus fully portable now
    - added new custom file loading dialog; allows non-modal usage, screenshot previews and ROM info summary, among many other benefits
    - added support for IPS soft-patching
    - added blargg's File_Extractor library
    - added support for archives compressed using 7-zip, RAR and BZip2; which is in addition to existing support for Gzip, ZIP and JMA
    - state manager now properly updates the timestamp column on saves [FitzRoy]
    - added OpenGL renderer to OS X port
    - fixed system beep issue with keyboard input on OS X port
    - fixed menubar visibility issue on OS X port
    - fixed a Display handle leak on Linux port [snzzbk]
    - X-video driver now releases SHM memory properly upon exit [emon]
    - fixed Direct3D rendering issue that was blurring video on some cards [Fes]
    - enhanced window positioning code for all platforms
    - debugger is now GUI-driven instead of via command-line
    - memory hex editor is now fully usable
    - added PPU video RAM viewer to debugger
    - added S-CPU and S-SMP tracing capabilities to debugger
    - Qt version upgraded to 4.5.2, and compiled with optimizations enabled; runs faster but makes the binary slightly larger
    - too many code cleanups to list
2009-09-27 11:40:16 +00:00
byuu
c2453cb634 Update to bsnes v050 release.
I always regret having to post new releases so quickly, but a semi-major bug crept into v049. I'd rather fix it now, before I start making major changes that will need testing again. The problem was that the S-PPU was not being synchronized as often as it should have been, resulting in titles such as F-Zero and Super Mario Kart showing flickering lines here and there. This release fixes that.
This release also adds savestate support for Mega Man X2 and Mega Man X3, which utilize the Cx4 coprocessor; and it fixes a bug where input was still accepted even when the main window was minimized.
2009-08-25 16:00:26 +00:00
byuu
59b86cd3a8 Update to bsnes v049 release.
This is a maintenance release, but it offers a lot of bug-fixes and speed-ups, so it should be well worth the update. The debugger is not finished yet, so use it at your own risk. It is disabled in the binary release because breakpoint testing impacts performance. Once it is ready, I will release a separate binary with the debugger enabled.
Changelog:
    - Optimized S-PPU emulation, provides a ~10-15% speedup in normal games
    - Cleaned up cheat editor user interface
    - Added save state and export data path selections
    - Added workaround for a strange issue that caused PAL games to run at 60 fps sometimes
    - Fixed sprite caching issue; fixes SD F-1 Grand Prix
    - Fixed PPUcounter reset issue; fixes Bishoujo Janshi Suchie-Pai [Jonas Quinn]
    - Fixed scaling on scanline, Scale2x, LQ2x and HQ2x filters on hires and interlace screens
    - Fixed sizeof(bool) serialization issue for PowerPC architecture [Richard Bannister]
    - Fixed cheat code sort ordering
    - Fixed a bug with centering in fullscreen mode
    - Fixed an audio pitch bug when changing frequency
    - Fixed a volume adjust bug when frequency was exactly 32000hz
    - Fixed X-video RGB rendering bugs [thanks to tukuyomi for testing]
    - Fixed a file open dialog issue on Linux when using QGtkStyle [jensbw]
    - Fixed a memory corruption issue involving QApplication::main() [giovannibajo]
    - Added a preliminary debugger (disabled in binary releases due to associated speed hit)
    - Added S-CPU and S-SMP stepping and tracing support
    - Added read/write/execute breakpoint support
    - Added memory editor (currently it can only view memory)
    - Added screenshot capture support [kode54]
    - Save state archives are now ~60% smaller than before
    - Various code cleanup work, as usual (note: the debugger code is messy, as it is in-progress)
2009-08-22 12:09:19 +00:00
byuu
c26f9d912a Update to bsnes v048 release.
The biggest feature of this new release is the addition of save state support. Note that this is only currently supported for normal games, and the SPC7110 and OBC-1 co-processors. Other special chips, such as the SuperFX and SA-1, cannot currently save and load state files. I will be adding support for other co-processors little by little in future releases.
Changelog:
    - Added save state support
    - Added SPC7110 and OBC1 save state support
    - Added new tools group, with new cheat code and save state managers
    - Lots of new UI shortcuts: quick save state, quick load state, show state manager, etc
    - Escape key will now close both the settings and tools group windows
    - Added major speed-ups to both SuperFX and SA-1 emulation; both now run ~15-25% faster than v047
    - Added new video filter, LQ2x; it's as fast as Scale2x while being almost as smooth as HQ2x
    - Re-wrote HQ2x algorithm; code size was reduced to less than 10% of its original size with virtually no speed loss
    - Corrected SuperFX2 cache access timing; fixes Stunt Race FX menus and slowdown in other titles
    - Relaxed palette write limitations for PGA Tour Golf [Jonas Quinn]
    - Fixed a slight timing issue that was breaking 'An Americal Tail - Feivel Goes West'
    - Turned off auto-save of SRAM as it was causing slowdowns when writing to flash memory; can be re-enabled via bsnes.cfg -> system.autoSaveMemory = true
    - Added bsnes.cfg -> system.autoHideMenus, defaults to false; when true, menu and status bars will be hidden upon entering fullscreen mode
    - Added skeletons for ST011 and ST018 support. Both Quick-move titles get in-game now
    - Re-wrote S-CPU and S-SMP processor cores to use templates, removed custom pre-processor
    - Split PPUcounter into a base class inherited by both PPU and CPU; allows both cores to run out-of-order
    - Split inline header functions to separate files, allows headers to be included in any order now
2009-07-12 09:45:57 +00:00
byuu
7b0e484c18 Update to bsnes v047 release.
The most notable feature for this release is the addition of SuperFX support. This enables an additional eight commercial games, and two unreleased betas, to run with full support. Most notably of these would be Super Mario World 2: Yoshi's Island and Starfox. Though timing is not quite perfect just yet, there should be no known issues with any titles at the time of this release. That means there should only be two official, commercially-released titles that are not compatible with bsnes at this time: Quick-move Shogi Match with Nidan Rank-holder Morita 1 and 2 (using the ST011 and ST018 co-processors, respectively.)
SuperFX support was the work of many people. GIGO was a great help by providing the source code to his SuperFX emulator (for reference; the implementation in bsnes is my own design), _Demo_ was very helpful in getting Starfox to work properly, and Jonas Quinn provided roughly a half-dozen very important bug fixes that affected nearly every SuperFX game. Without them, this release would not be possible. So please do thank them if you appreciate SuperFX support in bsnes.
Please note that SuperFX emulation is very demanding. I hate to have to repeat this, but once again: bsnes is a reference emulator. It exists to better understand the SNES hardware. It is written in such a manner as to be friendly to other developers (both emulator authors and game programmers), and the findings are meant to help improve other emulators. As far as I know, bsnes is the first emulator to fully support all SuperFX caching mechanisms (instruction cache, both pixel caches, ROM and RAM buffering caches, ...); as well as many other obscure features, such as full support for ROM / RAM access toggling between the SNES and SuperFX CPUs, and multiplier overhead timing. By emulating these, I was able to discover what additional components are needed to emulate Dirt Racer and Power Slide, two titles that no emulator has yet been able to run (they aren't very good games, you weren't missing much.) It should be possible to backport these fixes to faster emulators now.
That said, with a Core 2 Duo E8400 @ 3GHz, on average I get ~100fps in Super Mario World 2, ~95fps in Starfox and ~85fps in Doom. Compare this to ~165fps in Zelda 3, a game that does not use the SuperFX chip. My binary releases also target 32-bit x86 architecture. For those capable of building 64-bit binaries, especially Linux users, that should provide an additional ~10% speedup. Be sure to profile the application if you build it yourself.
Lastly on the SuperFX front, note that Starfox 2 is fully playable, but that most images floating around have corrupted headers. I do not attempt to repair bad headers, so these images will not work. Please either use NSRT on the Japanese version, or use Gideon Zhi's English fan translation patch, if you are having trouble running this title.
With that out the way, a few other improvements have been made to this release: xinput1_3.dll is no longer required for the Windows port (though you will need it if you want to use an Xbox 360 controller), the video drivers in ruby now allocate the smallest texture size possible for blitting video, and the code has been updated with preliminary compilation support for Mac OS X. Note that I will not be releasing binaries for this: it is primarily meant for developers and for porting my other libraries to the platform. Richard Bannister maintains a much better OS X port with full EE support and a native Apple GUI that follows their interface guidelines much better than a Qt port ever could. He has also synced the Mac port with this release. You can find a link to that in the bsnes download section.
2009-06-07 11:57:05 +00:00
byuu
f8e425ff49 Update to bsnes v046a release.
[No changelog available]
2009-05-12 02:33:49 +00:00
byuu
2a6a66f478 Update to bsnes v046 release.
Unfortunately, I was not able to include any actual Super Game Boy support in this release. I was however able to back-port all other changes since v045, as well as add a lot of new stuff. Though there are few visible changes from the last release, internally much has changed. I'm releasing this mostly as a point release whilst everything should be stable.
I've decided to support the Super Game Boy via external DLL (or SO for Linux users.) There are many reasons for this. Most notably is that the largest special chip in bsnes right now weighs in at ~30kb of code. Emulating an entire Game Boy, not including the SGB enhancements, would require an additional ~800kb of code, or nearly half the size of the entire SNES emulation core. Add to that potential issues with licensing, conflicts with the build process / namespace, a significant increase to build time, and a lack of flexibility over which Game Boy emulator to use, and it's pretty clear that this is something best left external. At least until we have a fully trimmed, fully working SGB emulator available.
The way this will work is bsnes will look for SuperGameBoy.(dll,so), and if present, it will call out to pre-defined functions. Users will need the SGB BIOS loaded, at which point they can select a Game Boy cartridge, and bsnes will use the DLL for actual emulation. Sadly I don't have a working DLL ready for this release, and even if I did, there's no sound bridge yet for the Game Boy audio.
Other than that, much of the core has been updated in an attempt to make the core more library-like. It still has a few major limitations: it requires libco (which is not portable) and nall (which is quite large), and only one instance can be instantiated as all of the base objects are pre-defined and inter-linked. Not that I can imagine any practical use for multiple simultaneous SNES emulators anyway ...
Changelog:
    - Save RAM is now automatically saved once per minute
    - Added delay to Super Scope / Justifier latching to fix X-Zone
    - Fixed an edge case in CPU<>PPU counter history
    - S-CPU can now run up to one full scanline ahead of S-PPU before syncing
    - Added interface for Super Game Boy support (no emulation yet)
    - Fixed a bug with path selection not adding trailing slash
    - All S-SMP opcodes re-written to use new pre-processor
    - Entire core encapsulated into SNES namespace
    - Core accepts files via memory only; zlib and libjma moved outside of core
    - Major Makefile restructuring: it's now possible to build with just "make" alone
    - Linux: libxtst / inputproto is no longer required for compilation
    - Lots of additional code cleanup
2009-05-10 11:01:02 +00:00
byuu
3c42e6caa0 Update to bsnes v045r09 release.
[No changelog available]
2009-04-30 20:58:39 +00:00
byuu
5f96547beb Update to bsnes v045 release.
This is a maintenance release to fix a crashing bug in S-DD1 games (Star Ocean, Street Fighter Alpha 2), and a video issue in games using the WAI instruction.
As always, my apologies for any inconvenience. SA-1 support required modification of a large amount of delicate code in the emulation core, and our limited testing team was not able to catch these in time before release.
2009-04-20 02:55:33 +00:00
byuu
44b5f1bf27 Update to bsnes v044 release.
This release adds full SA-1 support, with no known issues. All 26 games have been tested by myself and others, and a few have been beaten from start to finish. The latter include Super Mario RPG, Kirby's Dreamland 3, Kirby Super Star and Jikkyou Oshaberi Parodius.
Please understand that the SA-1 is essentially four times faster than the SNES' main CPU, so system requirements will be very high for these games. For example, on an E8400 @ 3.0GHz, I average ~160fps in ordinary games. But for SA-1 emulation, this drops to ~90fps, with the worst case being ~80fps.
The following features are emulated:
    - 5a22 CPU core (bus-cycle accurate)
    - Memory access timing
    - SA-1 -> S-CPU interrupts (IRQ + CHDMA IRQ)
    - S-CPU -> SA-1 interrupts (IRQ + Timer IRQ + DMA IRQ + NMI)
    - SIV / SNV interrupt vector selection
    - Timer unit (linear and H/V)
    - Super MMC unit (ROM + BW-RAM)
    - BS-X flash cart slot mapping
    - Normal DMA
    - Character-conversion 1 DMA (2bpp + 4bpp + 8bpp)
    - Character-conversion 2 DMA (2bpp + 4bpp + 8bpp)
    - BW-RAM virtual bitmap mode (2bpp + 4bpp)
    - Arithmetic unit (multiplication + division + cumulative sum)
    - Variable-length bit processing (fixed and auto increment)
While the following features are not currently emulated, mostly due to lack of information:
    - SA-1 bus conflict delays
    - Write protection (BW-RAM + I-RAM)
    - SA-1 CPU priority for DMA transfers
    - DMA access timing
2009-04-19 21:34:23 +00:00
byuu
b0a8de0208 Update to bsnes v043 release.
[No changelog available]
2009-04-18 17:13:29 +00:00
byuu
11e0a2ac18 Update to bsnes v042r05? release.
New WIP. Wasted two and a half hours trying to figure out why re-
implementing IRQs at home was failing in Parodius. Finally just
reverted to wip05 and started again, changing one line at a time.
Turns out I inverted the reset release flag by mistake for the SA-1
CPU. Fun.

Adds S-CPU -> SA-1 IRQs, DMA IRQs and NMIs + SA-1 -> S-CPU IRQs +
CH1DMA IRQs. Also slightly improves variable bit-length reading and
removes DPRIO mode for now until I can test it properly.

Parodius, SRW: Gaiden and Kirby: SS should all be fully playable now.

Mario RPG is damn close, but it freezes immediately after you exit the
level up bonus screen. I don't have any idea what it wants. The
graphics on the bonus screen don't show up either, as I don't support
char conversion modes 1 or 2 yet (it uses mode 1.)

How annoying ... first the graphics on the logo are bad. Add the ALU,
good. Now the title screen background is black. Fix the ALU MA
register reset, good. Now it freezes after the first intro scene. Add
SA-1 -> S-CPU IRQs. Now it freezes half-way through the intro. Fix
S-CPU /IRQ line holding from the SA-1. Now it freezes at the start of
the level up bonus screen. Add CHDMA IRQs. Now it freezes immediately
after the level up bonus screen.

I have no idea what the hell SIV / SNV are for. I'm guessing the SA-1
controller detects which processor activates SA-1 IRQs and uses that
vector address ...? It obviously can't over-ride the S-CPU's vector
addresses.

Documentation is shit. It doesn't specify what vectors DMA / CHDMA
use, or what to do without specific general DMA / CHDMA IRQ enable
flags in the control registers, and on and on.

[No archive available]
2009-04-10 13:52:00 +00:00
byuu
3a6eb56cef Update to bsnes v042r04? release.
New WIP. Copy-paste:
> Working on SA-1, still a long way to go. Fixed a bug where I was
> clearing MA after multiplication / cumulative sum when I wasn't
> supposed to. Fixes Kirby 3 Pop Star scene.

> Added normal DMA, along with full support for DPRIO (allowing DMA to
> run alongside the SA-1 CPU) and blocking of invalid transfer types /
> modes. This fixes sprites in Marvelous.

> Also added BW-RAM bitmap mirroring to $[60-6f]:[0000-ffff], proper
> mapping for the bitmap mode to the $[00-3f|80-bf]:[6000-7fff]
> regions, variable-length bit read data port, and I now at least
> cache the register settings for IRQs (though I still do nothing with
> them.)

> I added support for BW-RAM and I-RAM write protection, but when it's
> enabled, most games will no longer load. So I'm forced to leave that
> off for now. Maybe the protection didn't actually work on the real
> hardware? Hmm ...

> No idea what the bitmap registers $2240-$224f are for, and I don't
> see how it's supposed to be possible to trigger IRQs as needed by
> Super Mario RPG and Parodius. But at least three of five games
> should now be fully playable with no issues. Speed remains the same
> as yesterday. No hit for the SA-1 CPU+DMA simultaneous transfer mode
> support.


Image Image

> I want pictures of SRW Gaiden!


Can always try it and see what happens ... after I get some sleep :D

[No archive available]
2009-04-08 12:22:00 +00:00
byuu
4c92d11d80 Update to bsnes v042r03? release.
I mentioned I wouldn't be posting a new WIP for a while so that I
could work on something in secret. That way in case it didn't work
out, nobody would be bummed out. Imagine my surprise when it only took
me two days to get this far ...

Image Image
Image Image
Image Image
(I removed the title-bar text for the sake of the screenshot
aesthetic. Check the WIP yourself if you don't believe it.)

Kirby's Dream Land 3 and Dragon Ball Z: Hyper Dimension are fully
playable. Note that most games aren't playable, and most of the chip's
added features are missing.

Speed took a ~3-5% hit for non-SA1 games due to all the new co-
processor thread synchronization primitives that you can't really hide
from inlined, super-intensive sections of the scheduler code.

As of now, and this will change, SA-1 games run about ~60% slower than
normal games. Meaning you'll really want at least an E4500, but
preferrably an E8400; and no filters.

The most impressive part is that I emulate this at the bus/clock
level. Meaning if both the S-CPU and SA-1 access RAM at the same time,
they'll see the changes and stay perfectly in sync. I even emulated
the bus conflict resolution of the SA-1 memory controller. So in terms
of accuracy, this is akin to the cycle-level S-PPU. It's the
"theoretical worst case" for the most processor-intensive, lowest-
possible emulation achievable.

I believe it was _Demo_ who speculated that it'd take at least a 10GHz
processor to achieve this. Then again, it's been so long I could be
attributing the quote to the wrong person. Don't even remember the
exact words anymore. Anyone recall?

This gives us insight into the kind of performance we can expect from
the cycle-PPU (also runs at 10.74MHz) and SuperFX. For SA-1+cycle
S-PPU, it would appear that there is no processor on the market that
can maintain full speed with that combo yet, heh. By the time I get
around to S-PPU, there most likely will be though.

Lastly, don't bug me about SuperFX support because of this. This SA-1
support is a simple subclass of the core S-CPU that already existed in
cycle-perfect, bug-free form; plus a memory mapper and ALU. Lots more
to go, and even then, this is easily multiple times less work than the
SuperFX is going to be.

[No archive available]
2009-04-07 13:14:00 +00:00
byuu
f3d1d10d3e Update to bsnes v042r02? release.
New WIP. The entire S-CPU opcode core has been re-written to use my
new pre-processor.

The downside is that it's actually slightly slower, by less than 1%.
Guessing that having almost twice the opcode implementations ends up
eating more valuable L1 cache, making it more painful than the two
conditionals per function I had before. But damn if it isn't more
readable now.

Before:
    ror_addrx(0x7e, ror) {
    1:aa.l = op_readpc();
    2:aa.h = op_readpc();
    3:op_io();
    4:rd.l = op_readdbr(aa.w + regs.x.w);
    5:if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1);
    6:op_io();
      if(regs.p.m) { op_$1_b(); }
      else { op_$1_w();
    7:op_writedbr(aa.w + regs.x.w + 1, rd.h); }
    8:last_cycle();
      op_writedbr(aa.w + regs.x.w,     rd.l);
    }


After:
    @macro op_adjust_addrx(name)
      void {class}::op_{name}_addrx_b() {
        aa.l = op_readpc();
        aa.h = op_readpc();
        op_io();
        rd.l = op_readdbr(aa.w + regs.x.w);
        op_io();
        op_{name}_b();
    {lc}op_writedbr(aa.w + regs.x.w, rd.l);
      }

      void {class}::op_{name}_addrx_w() {
        aa.l = op_readpc();
        aa.h = op_readpc();
        op_io();
        rd.l = op_readdbr(aa.w + regs.x.w + 0);
        rd.h = op_readdbr(aa.w + regs.x.w + 1);
        op_io();
        op_{name}_w();
        op_writedbr(aa.w + regs.x.w + 1, rd.h);
    {lc}op_writedbr(aa.w + regs.x.w + 0, rd.l);
      }
    @endmacro

( note: {lc} is short-hand to 'hide' last_cycle(); )

Really worn out now, so don't expect a new WIP for quite a long time
I'm afraid. I'll worry about the S-SMP's core much later. Would
appreciate thorough testing. Given I rewrote all 256 opcodes by hand,
it's possible I made a mistake somewhere.

> Once Alt has been pressed to access the menubar (even just one
> time), the menu accelerator keys become functional even without
> pressing them together with Alt.


Wow ... that is quite alarming. Not sure why Qt is doing that. But
since I don't have a way of fixing it yet ... for now:

> Stop pressing alt.


:/

[No archive available]
2009-04-06 04:13:00 +00:00
byuu
90aa780d57 Update to bsnes v042r01? release.
New WIP.

Updated centering code, it now just has per-platform centering code.
So it should look great with no flickering / movement on Windows or
Linux.

Fixed the patching status thing so it won't say it patched when it
fails. But seems there's not enough safeties in nall::ups. A patch of
nothing but "UPS1" has a 50% chance of crashing the emulator.

Most importantly, I finally got around to writing my pre-processor,
which is intended to add macro support to both C++ and xkas. Calling
it bpp, for **b**yuu's **p**re-**p**rocessor.

I started rewriting the S-CPU opcodes to use the new pre-processor. 40
of 256 opcodes finished. I'm also separating the 8-bit and 16-bit
versions this time. Twice the code, but it's easier on the eyes.

Old:
    ldy_addrx(0xbc, ldy, regs.p.x),
    ora_addrx(0x1d, ora, regs.p.m),
    sbc_addrx(0xfd, sbc, regs.p.m) {
    1:aa.l = op_readpc();
    2:aa.h = op_readpc();
    3:op_io_cond4(aa.w, aa.w + regs.x.w);
    4:if($2) last_cycle();
      rd.l = op_readdbr(aa.w + regs.x.w);
      if($2) { op_$1_b(); end; }
    5:last_cycle();
      rd.h = op_readdbr(aa.w + regs.x.w + 1);
      op_$1_w();
    }


New:
    @macro op_read_addrx(name)
      void {class}::op_{name}_addrx_b() {
        aa.l = op_readpc();
        aa.h = op_readpc();
        op_io_cond4(aa.w, aa.w + regs.x.w);
        last_cycle();
        rd.l = op_readdbr(aa.w + regs.x.w);
        op_{name}_b();
      }

      void {class}::op_{name}_addrx_w() {
        aa.l = op_readpc();
        aa.h = op_readpc();
        op_io_cond4(aa.w, aa.w + regs.x.w);
        rd.l = op_readdbr(aa.w + regs.x.w + 0);
        last_cycle();
        rd.h = op_readdbr(aa.w + regs.x.w + 1);
        op_{name}_w();
      }
    @endmacro


    @global class sCPU
    @include "opcode_read.bpp"

    @op_read_addry(ldx)
    @op_read_addry(ora)
    @op_read_addry(sbc)


Yes, I know the above can be done with the C pre-processor. Two major
reasons I avoided it:
1) I refuse to put \ after every line.
2) parameters are limited, eg MACRO(&=~, x += 2) would not work.

The important thing was making a more generic / flexible format. Will
allow me to kill off src/tool, though I'll still include the new
parser's source under src/lib/bpp.

May extend bpp in the future, who knows. @if/@else/@endif would be
nice, as would nested macros and static programming functions.

[No archive available]
2009-04-03 11:15:00 +00:00
604 changed files with 43880 additions and 57668 deletions

View File

@@ -1,69 +1,53 @@
include lib/nall/Makefile.string
prefix = /usr/local
include lib/nall/Makefile
include lib/nall/Makefile-qt
ui = ui_qt
################
### compiler ###
################
ifneq ($(findstring gcc,$(compiler)),) # GCC family
flags = -O3 -fomit-frame-pointer -Ilib
# note: libco *requires* -fomit-frame-pointer on i386 arch
libcoflags := $(flags) -static
c = $(compiler)
cpp = $(subst cc,++,$(compiler))
obj = o
rule = -c $< -o $@
link = -s
mkbin = -o$1
mkdef = -D$1
mkincpath = -I$1
mklib = -l$1
mklibpath = -L$1
c := $(compiler)
cpp := $(subst cc,++,$(compiler))
flags := -O3 -fomit-frame-pointer -Ilib
link :=
# profile-guided optimization:
# profile-guided instrumentation:
# flags += -fprofile-generate
# link += -lgcov
# profile-guided optimization:
# flags += -fprofile-use
else ifeq ($(compiler),cl) # Visual C++
flags = /nologo /wd4355 /wd4805 /wd4996 /Ox /GL /EHsc /Ilib
libcoflags = $(flags)
c = cl
cpp = cl
obj = obj
rule = /c $< /Fo$@
link = /link
mkbin = /Fe$1
mkdef = /D$1
mkincpath = /I$1
mklib = $1.lib
mklibpath = /L$1
else
unknown_compiler: help;
endif
##########
### os ###
##########
################
### platform ###
################
ifeq ($(platform),x) # X11
ruby = video.glx video.xv video.sdl audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao input.sdl input.x
delete = rm -f $1
else ifeq ($(platform),win) # Windows
mingw_link_flags = -mwindows
# mingw_links_flags = -mconsole
ifeq ($(platform),x)
link += -s
# enable static linking to Qt for Windows build
mingw_link_flags += -enable-stdcall-fixup -Wl,-s -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
ruby := video.glx video.xv video.qtraster video.sdl
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.x
ruby = video.direct3d video.wgl video.directdraw video.gdi audio.directsound input.rawinput input.directinput
delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1))
link += $(if $(findstring mingw,$(compiler)),$(mingw_link_flags))
link += $(call mklib,uuid)
link += $(call mklib,kernel32)
link += $(call mklib,user32)
link += $(call mklib,gdi32)
link += $(call mklib,shell32)
link += $(if $(findstring audio.openal,$(ruby)),-lopenal)
else ifeq ($(platform),osx)
ruby := video.qtopengl video.qtraster
ruby += audio.openal
ruby += input.carbon
link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
else ifeq ($(platform),win)
link += -mwindows -mthreads
# link += -mconsole -mthreads
link += -s -luuid -lkernel32 -luser32 -lgdi32 -lshell32
# statically link Qt for Windows build
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
ruby := video.direct3d video.wgl video.directdraw video.gdi video.qtraster
ruby += audio.directsound
ruby += input.rawinput input.directinput
link += $(if $(findstring audio.openal,$(ruby)),-lopenal32)
else
unknown_platform: help;
endif
@@ -72,40 +56,32 @@ endif
### ruby ###
############
rubyflags = $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
link += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
rubyflags := $(call ifhas,.sdl,$(ruby),`sdl-config --cflags`)
rubyflags += $(call ifhas,.qt,$(ruby),$(qtinc))
link += $(if $(findstring video.direct3d,$(ruby)),$(call mklib,d3d9))
link += $(if $(findstring video.directdraw,$(ruby)),$(call mklib,ddraw))
link += $(if $(findstring video.glx,$(ruby)),$(call mklib,GL))
link += $(if $(findstring video.wgl,$(ruby)),$(call mklib,opengl32))
link += $(if $(findstring video.xv,$(ruby)),$(call mklib,Xv))
link += $(if $(findstring audio.alsa,$(ruby)),$(call mklib,asound))
link += $(if $(findstring audio.ao,$(ruby)),$(call mklib,ao))
link += $(if $(findstring audio.directsound,$(ruby)),$(call mklib,dsound))
link += $(if $(findstring audio.openal,$(ruby)),$(if $(call streq,$(platform),x),$(call mklib,openal),$(call mklib,openal32)))
link += $(if $(findstring audio.pulseaudio,$(ruby)),$(call mklib,pulse-simple))
link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid))
link += $(if $(findstring input.rawinput,$(ruby)),$(call mklib,xinput) $(call mklib,dinput8) $(call mklib,dxguid))
link += $(call ifhas,.sdl,$(ruby),`sdl-config --libs`)
link += $(call ifhas,video.direct3d,$(ruby),-ld3d9)
link += $(call ifhas,video.directdraw,$(ruby),-lddraw)
link += $(call ifhas,video.glx,$(ruby),-lGL)
link += $(call ifhas,video.wgl,$(ruby),-lopengl32)
link += $(call ifhas,video.xv,$(ruby),-lXv)
link += $(call ifhas,audio.alsa,$(ruby),-lasound)
link += $(call ifhas,audio.ao,$(ruby),-lao)
link += $(call ifhas,audio.directsound,$(ruby),-ldsound)
link += $(call ifhas,audio.pulseaudio,$(ruby),-lpulse-simple)
link += $(call ifhas,input.directinput,$(ruby),-ldinput8 -ldxguid)
link += $(call ifhas,input.rawinput,$(ruby),-ldinput8 -ldxguid)
####################
### core objects ###
####################
###############
### objects ###
###############
objects = libco ruby libfilter string \
reader cart cheat \
memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \
bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
ifeq ($(enable_gzip),true)
objects += adler32 compress crc32 deflate gzio inffast inflate inftrees ioapi trees unzip zip zutil
flags += $(call mkdef,GZIP_SUPPORT)
endif
ifeq ($(enable_jma),true)
objects += jma jcrc32 lzmadec 7zlzma iiostrm inbyte lzma winout
flags += $(call mkdef,JMA_SUPPORT)
endif
objects := libco ruby
objects += system cartridge cheat
objects += memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu
objects += supergameboy superfx sa1
objects += bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 st011 st018
objects += 21fx
######################
### implicit rules ###
@@ -114,134 +90,112 @@ endif
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(c) $(flags) $1 $(rule), \
$(c) $(flags) $1 -c $< -o $@, \
$(if $(filter %.cpp,$<), \
$(cpp) $(flags) $1 $(rule) \
$(cpp) $(flags) $1 -c $< -o $@ \
) \
) \
)
%.$(obj): $<; $(call compile)
%.o: $<; $(call compile)
all: build;
include $(ui)/Makefile
objects := $(patsubst %,obj/%.$(obj),$(objects))
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
objects := $(patsubst %,obj/%.o,$(objects))
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
#################
### libraries ###
#################
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/*
obj/ruby.o: lib/ruby/ruby.cpp $(call rwildcard,lib/ruby/*)
$(call compile,$(rubydef) $(rubyflags))
obj/libco.$(obj): lib/libco/libco.c lib/libco/*
$(c) $(libcoflags) $(rule)
obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/*
obj/string.$(obj): lib/nall/string.cpp lib/nall/*
obj/libco.o: lib/libco/libco.c lib/libco/*
#################
### utilities ###
#################
obj/reader.$(obj): reader/reader.cpp reader/*
obj/cart.$(obj) : cart/cart.cpp cart/*
obj/cheat.$(obj) : cheat/cheat.cpp cheat/*
obj/cartridge.o: cartridge/cartridge.cpp cartridge/*
obj/cheat.o : cheat/cheat.cpp cheat/*
##############
### memory ###
##############
obj/memory.$(obj) : memory/memory.cpp memory/*
obj/smemory.$(obj): memory/smemory/smemory.cpp memory/smemory/* memory/smemory/mapper/*
obj/memory.o : memory/memory.cpp memory/*
obj/smemory.o: memory/smemory/smemory.cpp $(call rwildcard,memory/smemory/)
###########
### cpu ###
###########
obj/cpu.$(obj) : cpu/cpu.cpp cpu/*
obj/scpu.$(obj): cpu/scpu/scpu.cpp cpu/scpu/* cpu/scpu/core/* cpu/scpu/dma/* cpu/scpu/memory/* cpu/scpu/mmio/* cpu/scpu/timing/*
obj/cpu.o : cpu/cpu.cpp cpu/*
obj/cpucore.o: cpu/core/core.cpp $(call rwildcard,cpu/core/)
obj/scpu.o : cpu/scpu/scpu.cpp $(call rwildcard,cpu/scpu/)
###########
### smp ###
###########
obj/smp.$(obj) : smp/smp.cpp smp/*
obj/ssmp.$(obj): smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/core/* smp/ssmp/memory/* smp/ssmp/timing/*
obj/smp.o : smp/smp.cpp smp/*
obj/smpcore.o: smp/core/core.cpp $(call rwildcard,smp/core/)
obj/ssmp.o : smp/ssmp/ssmp.cpp $(call rwildcard,smp/ssmp/)
###########
### dsp ###
###########
obj/adsp.$(obj): dsp/adsp/adsp.cpp dsp/adsp/*
obj/sdsp.$(obj): dsp/sdsp/sdsp.cpp dsp/sdsp/*
obj/adsp.o: dsp/adsp/adsp.cpp dsp/adsp/*
obj/sdsp.o: dsp/sdsp/sdsp.cpp dsp/sdsp/*
###########
### ppu ###
###########
obj/ppu.$(obj) : ppu/ppu.cpp ppu/*
obj/bppu.$(obj): ppu/bppu/bppu.cpp ppu/bppu/*
obj/ppu.o : ppu/ppu.cpp ppu/*
obj/bppu.o: ppu/bppu/bppu.cpp $(call rwildcard,ppu/bppu/)
############
### snes ###
############
##############
### system ###
##############
obj/snes.$(obj): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* snes/input/*
obj/system.o: system/system.cpp $(call rwildcard,system/)
#####################
### special chips ###
#####################
obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/*
obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/*
obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/*
obj/spc7110.$(obj): chip/spc7110/spc7110.cpp chip/spc7110/*
obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/*
obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/*
obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/*
obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/*
obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/*
obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/*
obj/st010.$(obj) : chip/st010/st010.cpp chip/st010/*
############
### zlib ###
############
obj/adler32.$(obj) : reader/zlib/adler32.c reader/zlib/*
obj/compress.$(obj): reader/zlib/compress.c reader/zlib/*
obj/crc32.$(obj) : reader/zlib/crc32.c reader/zlib/*
obj/deflate.$(obj) : reader/zlib/deflate.c reader/zlib/*
obj/gzio.$(obj) : reader/zlib/gzio.c reader/zlib/*
obj/inffast.$(obj) : reader/zlib/inffast.c reader/zlib/*
obj/inflate.$(obj) : reader/zlib/inflate.c reader/zlib/*
obj/inftrees.$(obj): reader/zlib/inftrees.c reader/zlib/*
obj/ioapi.$(obj) : reader/zlib/ioapi.c reader/zlib/*
obj/trees.$(obj) : reader/zlib/trees.c reader/zlib/*
obj/unzip.$(obj) : reader/zlib/unzip.c reader/zlib/*
obj/zip.$(obj) : reader/zlib/zip.c reader/zlib/*
obj/zutil.$(obj) : reader/zlib/zutil.c reader/zlib/*
###########
### jma ###
###########
obj/jma.$(obj) : reader/jma/jma.cpp reader/jma/*
obj/jcrc32.$(obj) : reader/jma/jcrc32.cpp reader/jma/*
obj/lzmadec.$(obj): reader/jma/lzmadec.cpp reader/jma/*
obj/7zlzma.$(obj) : reader/jma/7zlzma.cpp reader/jma/*
obj/iiostrm.$(obj): reader/jma/iiostrm.cpp reader/jma/*
obj/inbyte.$(obj) : reader/jma/inbyte.cpp reader/jma/*
obj/lzma.$(obj) : reader/jma/lzma.cpp reader/jma/*
obj/winout.$(obj) : reader/jma/winout.cpp reader/jma/*
obj/supergameboy.o: chip/supergameboy/supergameboy.cpp $(call rwildcard,chip/supergameboy/)
obj/superfx.o : chip/superfx/superfx.cpp $(call rwildcard,chip/superfx/)
obj/sa1.o : chip/sa1/sa1.cpp $(call rwildcard,chip/sa1/)
obj/bsx.o : chip/bsx/bsx.cpp chip/bsx/*
obj/srtc.o : chip/srtc/srtc.cpp chip/srtc/*
obj/sdd1.o : chip/sdd1/sdd1.cpp chip/sdd1/*
obj/spc7110.o : chip/spc7110/spc7110.cpp chip/spc7110/*
obj/cx4.o : chip/cx4/cx4.cpp chip/cx4/*
obj/dsp1.o : chip/dsp1/dsp1.cpp chip/dsp1/*
obj/dsp2.o : chip/dsp2/dsp2.cpp chip/dsp2/*
obj/dsp3.o : chip/dsp3/dsp3.cpp chip/dsp3/*
obj/dsp4.o : chip/dsp4/dsp4.cpp chip/dsp4/*
obj/obc1.o : chip/obc1/obc1.cpp chip/obc1/*
obj/st010.o : chip/st010/st010.cpp chip/st010/*
obj/st011.o : chip/st011/st011.cpp chip/st011/*
obj/st018.o : chip/st018/st018.cpp chip/st018/*
obj/21fx.o : chip/21fx/21fx.cpp chip/21fx/*
###############
### targets ###
###############
build: ui_build $(objects)
$(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link))
ifeq ($(platform),osx)
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
else
$(strip $(cpp) -o ../bsnes $(objects) $(link))
endif
install:
install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes
@@ -249,7 +203,7 @@ install:
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
clean: ui_clean
-@$(call delete,obj/*.$(obj))
-@$(call delete,obj/*.o)
-@$(call delete,*.res)
-@$(call delete,*.pgd)
-@$(call delete,*.pgc)
@@ -268,11 +222,6 @@ help:
@echo " gcc - GCC compiler"
@echo " mingw32-gcc - MinGW compiler"
@echo " i586-mingw32-gcc - MinGW cross compiler"
@echo " cl - Visual C++"
@echo ""
@echo "Available options:"
@echo " enable_gzip=[true|false] - Enable ZIP / GZ support (default=false)"
@echo " enable_jma=[true|false] - Enable JMA support (default=false)"
@echo ""
@echo "Example: $(MAKE) platform=x compiler=gcc enable_gzip=true"
@echo "Example: $(MAKE) platform=x compiler=gcc"
@echo ""

View File

@@ -1,36 +1,33 @@
#define BSNES_VERSION "0.042"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus
#define CPUCORE sCPU
#define SMPCORE sSMP
#define DSPCORE sDSP
#define PPUCORE bPPU
static const char bsnesVersion[] = "0.059";
static const char bsnesTitle[] = "bsnes";
static const unsigned bsnesSerializerVersion = 4;
//S-DSP can be encapsulated into a state machine using #define magic
//this avoids ~2.048m co_switch() calls per second (~5% speedup)
#define USE_STATE_MACHINE
//FAST_FRAMESKIP disables calculation of RTO during frameskip
//frameskip offers near-zero speedup if RTO is calculated
//accuracy is not affected by this define when frameskipping is off
#define FAST_FRAMESKIP
#define DSP_STATE_MACHINE
//game genie + pro action replay code support (~2% speed hit)
#define CHEAT_SYSTEM
//enable debugging extensions (~15% speed hit)
//#define DEBUGGER
#include <libco/libco.h>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bit.hpp>
#include <nall/detect.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/moduloarray.hpp>
#include <nall/new.hpp>
#include <nall/platform.hpp>
#include <nall/priorityqueue.hpp>
#include <nall/property.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>

Binary file not shown.

View File

@@ -1,234 +0,0 @@
#include <../base.hpp>
#include <../chip/chip.hpp>
#include <../reader/reader.hpp>
#define CART_CPP
#include <nall/crc32.hpp>
#include <nall/ups.hpp>
#include "cart.hpp"
#include "cart_file.cpp"
#include "cart_header.cpp"
#include "cart_loader.cpp"
namespace memory {
MappedRAM cartrom, cartram, cartrtc;
MappedRAM bscram;
MappedRAM stArom, stAram;
MappedRAM stBrom, stBram;
};
Cartridge cartridge;
void Cartridge::load_begin(Mode cartridge_mode) {
cart.rom = cart.ram = cart.rtc = 0;
bs.ram = 0;
stA.rom = stA.ram = 0;
stB.rom = stB.ram = 0;
cart.rom_size = cart.ram_size = cart.rtc_size = 0;
bs.ram_size = 0;
stA.rom_size = stA.ram_size = 0;
stB.rom_size = stB.ram_size = 0;
set(loaded, false);
set(bsx_flash_loaded, false);
set(patched, false);
set(mode, cartridge_mode);
}
void Cartridge::load_end() {
memory::cartrom.map(cart.rom, cart.rom_size);
memory::cartram.map(cart.ram, cart.ram_size);
memory::cartrtc.map(cart.rtc, cart.rtc_size);
memory::bscram.map(bs.ram, bs.ram_size);
memory::stArom.map(stA.rom, stA.rom_size);
memory::stAram.map(stA.ram, stA.ram_size);
memory::stBrom.map(stB.rom, stB.rom_size);
memory::stBram.map(stB.ram, stB.ram_size);
memory::cartrom.write_protect(true);
memory::cartram.write_protect(false);
memory::bscram.write_protect(true);
memory::stArom.write_protect(true);
memory::stAram.write_protect(false);
memory::stBrom.write_protect(true);
memory::stBram.write_protect(false);
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
if(file::exists(cheat_file)) {
cheat.clear();
cheat.load(cheat_file);
}
bus.load_cart();
set(loaded, true);
}
void Cartridge::unload() {
if(loaded() == false) return;
bus.unload_cart();
switch(mode()) {
case ModeNormal: unload_normal(); break;
case ModeBsxSlotted: unload_bsx_slotted(); break;
case ModeBsx: unload_bsx(); break;
case ModeSufamiTurbo: unload_sufami_turbo(); break;
}
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
if(cart.ram) { delete[] cart.ram; cart.ram = 0; }
if(cart.rtc) { delete[] cart.rtc; cart.rtc = 0; }
if(bs.ram) { delete[] bs.ram; bs.ram = 0; }
if(stA.rom) { delete[] stA.rom; stA.rom = 0; }
if(stA.ram) { delete[] stA.ram; stA.ram = 0; }
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
if(cheat.count() > 0 || file::exists(cheat_file)) {
cheat.save(cheat_file);
cheat.clear();
}
set(loaded, false);
}
Cartridge::Cartridge() {
set(loaded, false);
}
Cartridge::~Cartridge() {
if(loaded() == true) unload();
}
void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) {
set(region, source.region);
set(mapper, source.mapper);
set(dsp1_mapper, source.dsp1_mapper);
set(has_bsx_slot, source.bsx_slot);
set(has_superfx, source.superfx);
set(has_sa1, source.sa1);
set(has_srtc, source.srtc);
set(has_sdd1, source.sdd1);
set(has_spc7110, source.spc7110);
set(has_spc7110rtc, source.spc7110rtc);
set(has_cx4, source.cx4);
set(has_dsp1, source.dsp1);
set(has_dsp2, source.dsp2);
set(has_dsp3, source.dsp3);
set(has_dsp4, source.dsp4);
set(has_obc1, source.obc1);
set(has_st010, source.st010);
set(has_st011, source.st011);
set(has_st018, source.st018);
}
//==========
//cartinfo_t
//==========
void Cartridge::cartinfo_t::reset() {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
region = NTSC;
rom_size = 0;
ram_size = 0;
bsx_slot = false;
superfx = false;
sa1 = false;
srtc = false;
sdd1 = false;
spc7110 = false;
spc7110rtc = false;
cx4 = false;
dsp1 = false;
dsp2 = false;
dsp3 = false;
dsp4 = false;
obc1 = false;
st010 = false;
st011 = false;
st018 = false;
}
Cartridge::cartinfo_t::cartinfo_t() {
reset();
}
//=======
//utility
//=======
//ensure file path is absolute (eg resolve relative paths)
string Cartridge::filepath(const char *filename, const char *pathname) {
//if no pathname, return filename as-is
string file(filename);
file.replace("\\", "/");
string path = (!pathname || !*pathname) ? (const char*)snes.config.path.current : pathname;
//ensure path ends with trailing '/'
path.replace("\\", "/");
if(!strend(path, "/")) path.append("/");
//replace relative path with absolute path
if(strbegin(path, "./")) {
ltrim(path, "./");
path = string() << snes.config.path.base << path;
}
//remove folder part of filename
lstring part;
part.split("/", file);
return path << part[part.size() - 1];
}
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
string Cartridge::basename(const char *filename) {
string name(filename);
//remove extension
for(signed i = strlen(name) - 1; i >= 0; i--) {
if(name[i] == '.') {
name[i] = 0;
break;
}
}
//remove directory information
for(signed i = strlen(name) - 1; i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') {
i++;
char *output = name();
while(true) {
*output++ = name[i];
if(!name[i]) break;
i++;
}
break;
}
}
return name;
}
//remove filename and return path only ("/foo/bar.ext" -> "/foo/bar/")
string Cartridge::basepath(const char *filename) {
string path(filename);
path.replace("\\", "/");
//remove filename
for(signed i = strlen(path) - 1; i >= 0; i--) {
if(path[i] == '/') {
path[i] = 0;
break;
}
}
if(!strend(path, "/")) path.append("/");
return path;
}

View File

@@ -1,178 +0,0 @@
class Cartridge : public property {
public:
enum Mode {
ModeNormal,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
};
enum Type {
TypeNormal,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeUnknown,
};
enum Region {
NTSC,
PAL,
};
enum MemoryMapper {
LoROM,
HiROM,
ExLoROM,
ExHiROM,
SPC7110ROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
enum DSP1MemoryMapper {
DSP1Unmapped,
DSP1LoROM1MB,
DSP1LoROM2MB,
DSP1HiROM,
};
//properties can be read via operator(), eg "if(cartridge.loaded() == true)";
//warning: if loaded() == false, no other property is considered valid!
property_t<bool> loaded; //is a base cartridge inserted?
property_t<bool> bsx_flash_loaded; //is a BS-X flash cart connected?
property_t<bool> patched; //has a UPS patch been applied?
property_t<string> name; //display name (filename sans path and extension)
property_t<Mode> mode;
property_t<Region> region;
property_t<MemoryMapper> mapper;
property_t<DSP1MemoryMapper> dsp1_mapper;
property_t<bool> has_bsx_slot;
property_t<bool> has_superfx;
property_t<bool> has_sa1;
property_t<bool> has_srtc;
property_t<bool> has_sdd1;
property_t<bool> has_spc7110, has_spc7110rtc;
property_t<bool> has_cx4;
property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
property_t<bool> has_obc1;
property_t<bool> has_st010, has_st011, has_st018;
//main interface
bool load_normal (const char *base);
bool load_bsx_slotted (const char *base, const char *slot = "");
bool load_bsx (const char *base, const char *slot = "");
bool load_sufami_turbo(const char *base, const char *slotA = "", const char *slotB = "");
void unload();
//utility functions
static string filepath(const char *filename, const char *pathname); //"./bar.ext" -> "/foo/bar.ext"
static string basename(const char *filename); //"/foo/bar.ext" -> "bar"
static string basepath(const char *filename); //"/foo/bar.ext" -> "/foo/bar/"
//this function will load 'filename', decompress it if needed, and determine what type of
//image file 'filename' refers to (eg normal cart, BS-X flash cart, Sufami Turbo cart, etc.)
//warning: this operation is very expensive, use sparingly!
Type detect_image_type(const char *filename) const;
Cartridge();
~Cartridge();
private:
void load_begin(Mode);
void load_end();
void unload_normal();
void unload_bsx_slotted();
void unload_bsx();
void unload_sufami_turbo();
struct cartinfo_t {
Type type;
Region region;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
unsigned rom_size, ram_size;
bool bsx_slot;
bool superfx;
bool sa1;
bool srtc;
bool sdd1;
bool spc7110, spc7110rtc;
bool cx4;
bool dsp1, dsp2, dsp3, dsp4;
bool obc1;
bool st010, st011, st018;
void reset();
cartinfo_t();
};
enum HeaderField {
CartName = 0x00,
Mapper = 0x15,
RomType = 0x16,
RomSize = 0x17,
RamSize = 0x18,
CartRegion = 0x19,
Company = 0x1a,
Version = 0x1b,
Complement = 0x1c, //inverse checksum
Checksum = 0x1e,
ResetVector = 0x3c,
};
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const;
unsigned find_header(const uint8_t *data, unsigned size) const;
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const;
void set_cartinfo(const cartinfo_t&);
bool load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const;
bool load_ram (const char *filename, uint8_t *&data, unsigned size, uint8_t init_value) const;
enum CompressionMode {
CompressionNone, //always load without compression
CompressionInspect, //use file header inspection
CompressionAuto, //use file extension or file header inspection (configured by user)
};
bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone) const;
bool save_file(const char *fn, uint8 *data, unsigned size) const;
bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size) const;
string modify_extension(const char *filename, const char *extension) const;
string get_filename(const char *source, const char *extension, const char *path) const;
struct {
string filename;
uint8_t *rom, *ram, *rtc;
unsigned rom_size, ram_size, rtc_size;
} cart;
struct {
string filename;
uint8_t *ram;
unsigned ram_size;
} bs;
struct {
string filename;
uint8_t *rom, *ram;
unsigned rom_size, ram_size;
} stA, stB;
};
namespace memory {
extern MappedRAM cartrom, cartram, cartrtc;
extern MappedRAM bscram;
extern MappedRAM stArom, stAram;
extern MappedRAM stBrom, stBram;
};
extern Cartridge cartridge;

View File

@@ -1,109 +0,0 @@
#ifdef CART_CPP
#include "../reader/filereader.hpp"
#if defined(GZIP_SUPPORT)
#include "../reader/gzreader.hpp"
#include "../reader/zipreader.hpp"
#endif
#if defined(JMA_SUPPORT)
#include "../reader/jmareader.hpp"
#endif
string Cartridge::modify_extension(const char *filename_, const char *extension) const {
string filename = filename_;
int i;
for(i = strlen(filename); i >= 0; i--) {
if(filename[i] == '.') break;
if(filename[i] == '/') break;
if(filename[i] == '\\') break;
}
if(i > 0 && filename[i] == '.') filename[i] = 0;
return filename << "." << extension;
}
string Cartridge::get_filename(const char *source, const char *extension, const char *path) const {
return filepath(modify_extension(source, extension), path);
}
bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) const {
if(file::exists(fn) == false) return false;
Reader::Type filetype = Reader::Normal;
if(compression == CompressionInspect) filetype = Reader::detect(fn, true);
if(compression == CompressionAuto) filetype = Reader::detect(fn, snes.config.file.autodetect_type);
switch(filetype) { default:
case Reader::Normal: {
FileReader ff(fn);
if(!ff.ready()) return false;
size = ff.size();
data = ff.read();
} break;
#ifdef GZIP_SUPPORT
case Reader::GZIP: {
GZReader gf(fn);
if(!gf.ready()) return false;
size = gf.size();
data = gf.read();
} break;
case Reader::ZIP: {
ZipReader zf(fn);
if(!zf.ready()) return false;
size = zf.size();
data = zf.read();
} break;
#endif
#ifdef JMA_SUPPORT
case Reader::JMA: {
try {
JMAReader jf(fn);
size = jf.size();
data = jf.read();
} catch(JMA::jma_errors jma_error) {
return false;
}
} break;
#endif
}
return true;
}
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) const {
uint8_t *outdata = 0;
unsigned outsize;
ups patcher;
ups::result result = patcher.apply(pdata, psize, data, size, outdata, outsize);
bool apply = false;
if(result == ups::ok) apply = true;
if(snes.config.file.bypass_patch_crc32 == true) {
if(result == ups::input_crc32_invalid) apply = true;
if(result == ups::output_crc32_invalid) apply = true;
}
//if patch application was successful, replace old data, size with new data, size
if(apply == true) {
delete[] data;
data = new uint8_t[size = outsize];
memcpy(data, outdata, outsize);
}
if(outdata) delete[] outdata;
return apply;
}
bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) const {
file fp;
if(!fp.open(fn, file::mode_write)) return false;
fp.write(data, size);
fp.close();
return true;
}
#endif

View File

@@ -1,244 +0,0 @@
#ifdef CART_CPP
//================
//Normal cartridge
//================
bool Cartridge::load_normal(const char *base) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
load_begin(ModeNormal);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(cartinfo.ram_size > 0) {
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
if(cartinfo.srtc || cartinfo.spc7110rtc) {
load_ram(get_filename(base, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size = 20, 0x00);
}
load_end();
set(name, basename(base));
return true;
}
void Cartridge::unload_normal() {
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
if(cart.rtc) save_file(get_filename(cart.filename, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size);
}
//======================
//BS-X slotted cartridge
//======================
bool Cartridge::load_bsx_slotted(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
bs.filename = slot;
load_begin(ModeBsxSlotted);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(load_image(slot, data, size, patch_applied) == true) {
set(bsx_flash_loaded, true);
if(patch_applied) set(patched, true);
bs.ram = data;
bs.ram_size = size;
}
if(cartinfo.ram_size > 0) {
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
load_end();
string filename = basename(base);
if(*slot) filename << " + " << basename(slot);
set(name, filename);
return true;
}
void Cartridge::unload_bsx_slotted() {
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
}
//====================
//BS-X flash cartridge
//====================
bool Cartridge::load_bsx(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
bs.filename = slot;
load_begin(ModeBsx);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
cart.ram = 0;
cart.ram_size = 0;
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
if(load_file(get_filename(base, "srm", snes.config.path.save), data, size, CompressionNone) == true) {
memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size));
delete[] data;
}
if(load_file(get_filename(base, "psr", snes.config.path.save), data, size, CompressionNone) == true) {
memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size));
delete[] data;
}
if(load_image(slot, data, size, patch_applied) == true) {
set(bsx_flash_loaded, true);
if(patch_applied) set(patched, true);
bs.ram = data;
bs.ram_size = size;
}
load_end();
set(name, !*slot ? basename(base) : basename(slot));
return true;
}
void Cartridge::unload_bsx() {
save_file(get_filename(cart.filename, "srm", snes.config.path.save), bsxcart.sram.handle (), bsxcart.sram.size ());
save_file(get_filename(cart.filename, "psr", snes.config.path.save), bsxcart.psram.handle(), bsxcart.psram.size());
}
//============================
//Sufami Turbo flash cartridge
//============================
bool Cartridge::load_sufami_turbo(const char *base, const char *slotA, const char *slotB) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
stA.filename = slotA;
stB.filename = slotB;
load_begin(ModeSufamiTurbo);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(load_image(slotA, data, size, patch_applied) == true) {
if(patch_applied) set(patched, true);
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
memcpy(stA.rom, data, min(size, stA.rom_size));
delete[] data;
load_ram(get_filename(slotA, "srm", snes.config.path.save), stA.ram, stA.ram_size = 0x020000, 0xff);
}
if(load_image(slotB, data, size, patch_applied) == true) {
if(patch_applied) set(patched, true);
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
memcpy(stB.rom, data, min(size, stB.rom_size));
delete[] data;
load_ram(get_filename(slotB, "srm", snes.config.path.save), stB.ram, stB.ram_size = 0x020000, 0xff);
}
load_end();
string filename;
if(!*slotA && !*slotB) filename << basename(base);
else if( *slotA && !*slotB) filename << basename(slotA);
else if(!*slotA && *slotB) filename << basename(slotB);
else filename << basename(slotA) << " + " << basename(slotB);
set(name, filename);
return true;
}
void Cartridge::unload_sufami_turbo() {
if(stA.ram) save_file(get_filename(stA.filename, "srm", snes.config.path.save), stA.ram, stA.ram_size);
if(stB.ram) save_file(get_filename(stB.filename, "srm", snes.config.path.save), stB.ram, stB.ram_size);
}
//=================
//utility functions
//=================
Cartridge::Type Cartridge::detect_image_type(const char *filename) const {
uint8_t *data;
unsigned size;
bool patch_applied;
if(!load_image(filename, data, size, patch_applied)) return TypeUnknown;
cartinfo_t info;
read_header(info, data, size);
delete[] data;
return info.type;
}
bool Cartridge::load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const {
if(!filename || !*filename) return false;
if(!load_file(filename, data, size, CompressionAuto)) return false;
if((size & 0x7fff) == 512) {
//remove 512-byte header
memmove(data, data + 512, size -= 512);
}
uint8_t *pdata;
unsigned psize;
if(load_file(get_filename(filename, "ups", snes.config.path.patch), pdata, psize, CompressionInspect) == true) {
apply_patch(pdata, psize, data, size);
delete[] pdata;
patched = true;
} else {
patched = false;
}
return true;
}
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) const {
data = new uint8_t[size];
memset(data, init, size);
uint8_t *savedata;
unsigned savesize;
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
memcpy(data, savedata, min(size, savesize));
delete[] savedata;
return true;
}
#endif

133
src/cartridge/cartridge.cpp Normal file
View File

@@ -0,0 +1,133 @@
#include <../base.hpp>
#include <nall/crc32.hpp>
#include <nall/sha256.hpp>
#define CARTRIDGE_CPP
namespace SNES {
#include "header.cpp"
#include "gameboyheader.cpp"
#include "serialization.cpp"
namespace memory {
MappedRAM cartrom, cartram, cartrtc;
MappedRAM bsxflash, bsxram, bsxpram;
MappedRAM stArom, stAram;
MappedRAM stBrom, stBram;
MappedRAM gbrom, gbram, gbrtc;
};
Cartridge cartridge;
void Cartridge::load(Mode cartridge_mode) {
mode = cartridge_mode;
read_header(memory::cartrom.data(), memory::cartrom.size());
if(ram_size > 0) {
memory::cartram.map(allocate<uint8_t>(ram_size, 0xff), ram_size);
}
if(has_srtc || has_spc7110rtc) {
memory::cartrtc.map(allocate<uint8_t>(20, 0xff), 20);
}
if(mode == ModeBsx) {
memory::bsxram.map (allocate<uint8_t>( 32 * 1024, 0xff), 32 * 1024);
memory::bsxpram.map(allocate<uint8_t>(512 * 1024, 0xff), 512 * 1024);
}
if(mode == ModeSufamiTurbo) {
if(memory::stArom.data()) memory::stAram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
if(memory::stBrom.data()) memory::stBram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
}
if(mode == ModeSuperGameBoy) {
if(memory::gbrom.data()) {
unsigned ram_size = gameboy_ram_size();
unsigned rtc_size = gameboy_rtc_size();
if(ram_size) memory::gbram.map(allocate<uint8_t>(ram_size, 0xff), ram_size);
if(rtc_size) memory::gbrtc.map(allocate<uint8_t>(rtc_size, 0x00), rtc_size);
}
}
memory::cartrom.write_protect(true);
memory::cartram.write_protect(false);
memory::cartrtc.write_protect(false);
memory::bsxflash.write_protect(true);
memory::bsxram.write_protect(false);
memory::bsxpram.write_protect(false);
memory::stArom.write_protect(true);
memory::stAram.write_protect(false);
memory::stBrom.write_protect(true);
memory::stBram.write_protect(false);
memory::gbrom.write_protect(true);
memory::gbram.write_protect(false);
memory::gbrtc.write_protect(false);
unsigned checksum = ~0;
for(unsigned n = 0; n < memory::cartrom.size(); n++) checksum = crc32_adjust(checksum, memory::cartrom[n]);
if(memory::bsxflash.size() != 0 && memory::bsxflash.size() != ~0)
for(unsigned n = 0; n < memory::bsxflash.size(); n++) checksum = crc32_adjust(checksum, memory::bsxflash[n]);
if(memory::stArom.size() != 0 && memory::stArom.size() != ~0)
for(unsigned n = 0; n < memory::stArom.size(); n++) checksum = crc32_adjust(checksum, memory::stArom[n]);
if(memory::stBrom.size() != 0 && memory::stBrom.size() != ~0)
for(unsigned n = 0; n < memory::stBrom.size(); n++) checksum = crc32_adjust(checksum, memory::stBrom[n]);
if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0)
for(unsigned n = 0; n < memory::gbrom.size(); n++) checksum = crc32_adjust(checksum, memory::gbrom[n]);
crc32 = ~checksum;
#if 0
fprintf(stdout, "crc32 = %.8x\n", (unsigned)crc32);
sha256_ctx sha;
uint8_t shahash[32];
sha256_init(&sha);
sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size());
sha256_final(&sha);
sha256_hash(&sha, shahash);
fprintf(stdout, "sha256 = ");
for(unsigned i = 0; i < 32; i++) fprintf(stdout, "%.2x", shahash[i]);
fprintf(stdout, "\n");
#endif
bus.load_cart();
system.serialize_init();
loaded = true;
}
void Cartridge::unload() {
memory::cartrom.reset();
memory::cartram.reset();
memory::cartrtc.reset();
memory::bsxflash.reset();
memory::bsxram.reset();
memory::bsxpram.reset();
memory::stArom.reset();
memory::stAram.reset();
memory::stBrom.reset();
memory::stBram.reset();
memory::gbrom.reset();
memory::gbram.reset();
memory::gbrtc.reset();
if(loaded == false) return;
bus.unload_cart();
loaded = false;
}
bool Cartridge::has_21fx() const {
return s21fx.exists();
}
Cartridge::Cartridge() {
loaded = false;
unload();
}
Cartridge::~Cartridge() {
unload();
}
}

116
src/cartridge/cartridge.hpp Normal file
View File

@@ -0,0 +1,116 @@
class Cartridge : property<Cartridge> {
public:
enum Mode {
ModeNormal,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
ModeSuperGameBoy,
};
enum Type {
TypeNormal,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeSuperGameBoy1Bios,
TypeSuperGameBoy2Bios,
TypeGameBoy,
TypeUnknown,
};
enum Region {
NTSC,
PAL,
};
enum MemoryMapper {
LoROM,
HiROM,
ExLoROM,
ExHiROM,
SuperFXROM,
SA1ROM,
SPC7110ROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
enum DSP1MemoryMapper {
DSP1Unmapped,
DSP1LoROM1MB,
DSP1LoROM2MB,
DSP1HiROM,
};
readonly<bool> loaded; //is a base cartridge inserted?
readonly<unsigned> crc32; //crc32 of all cartridges (base+slot(s))
readonly<Mode> mode;
readonly<Type> type;
readonly<Region> region;
readonly<MemoryMapper> mapper;
readonly<DSP1MemoryMapper> dsp1_mapper;
readonly<bool> has_bsx_slot;
readonly<bool> has_superfx;
readonly<bool> has_sa1;
readonly<bool> has_srtc;
readonly<bool> has_sdd1;
readonly<bool> has_spc7110;
readonly<bool> has_spc7110rtc;
readonly<bool> has_cx4;
readonly<bool> has_dsp1;
readonly<bool> has_dsp2;
readonly<bool> has_dsp3;
readonly<bool> has_dsp4;
readonly<bool> has_obc1;
readonly<bool> has_st010;
readonly<bool> has_st011;
readonly<bool> has_st018;
bool has_21fx() const;
void load(Mode);
void unload();
void serialize(serializer&);
Cartridge();
~Cartridge();
private:
enum HeaderField {
CartName = 0x00,
Mapper = 0x15,
RomType = 0x16,
RomSize = 0x17,
RamSize = 0x18,
CartRegion = 0x19,
Company = 0x1a,
Version = 0x1b,
Complement = 0x1c, //inverse checksum
Checksum = 0x1e,
ResetVector = 0x3c,
};
unsigned ram_size;
void read_header(const uint8_t *data, unsigned size);
unsigned find_header(const uint8_t *data, unsigned size) const;
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const;
unsigned gameboy_ram_size() const;
unsigned gameboy_rtc_size() const;
};
namespace memory {
extern MappedRAM cartrom, cartram, cartrtc;
extern MappedRAM bsxflash, bsxram, bsxpram;
extern MappedRAM stArom, stAram;
extern MappedRAM stBrom, stBram;
extern MappedRAM gbrom, gbram, gbrtc;
};
extern Cartridge cartridge;

View File

@@ -0,0 +1,22 @@
#ifdef CARTRIDGE_CPP
unsigned Cartridge::gameboy_ram_size() const {
if(memory::gbrom.size() < 512) return 0;
switch(memory::gbrom[0x0149]) {
case 0x00: return 0 * 1024;
case 0x01: return 8 * 1024;
case 0x02: return 8 * 1024;
case 0x03: return 32 * 1024;
case 0x04: return 128 * 1024;
case 0x05: return 128 * 1024;
default: return 128 * 1024;
}
}
unsigned Cartridge::gameboy_rtc_size() const {
if(memory::gbrom.size() < 512) return 0;
if(memory::gbrom[0x0147] == 0x0f || memory::gbrom[0x0147] == 0x10) return 4;
return 0;
}
#endif

View File

@@ -1,8 +1,53 @@
#ifdef CART_CPP
#ifdef CARTRIDGE_CPP
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const {
info.reset();
unsigned index = find_header(data, size);
void Cartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
region = NTSC;
ram_size = 0;
has_bsx_slot = false;
has_superfx = false;
has_sa1 = false;
has_srtc = false;
has_sdd1 = false;
has_spc7110 = false;
has_spc7110rtc = false;
has_cx4 = false;
has_dsp1 = false;
has_dsp2 = false;
has_dsp3 = false;
has_dsp4 = false;
has_obc1 = false;
has_st010 = false;
has_st011 = false;
has_st018 = false;
//=====================
//detect Game Boy carts
//=====================
if(size >= 0x0140) {
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
type = TypeGameBoy;
return;
}
}
const unsigned index = find_header(data, size);
const uint8 mapperid = data[index + Mapper];
const uint8 rom_type = data[index + RomType];
const uint8 rom_size = data[index + RomSize];
const uint8 company = data[index + Company];
const uint8 regionid = data[index + CartRegion] & 0x7f;
ram_size = 1024 << (data[index + RamSize] & 7);
if(ram_size == 1024) ram_size = 0; //no RAM present
//0, 1, 13 = NTSC; 2 - 12 = PAL
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
//=======================
//detect BS-X flash carts
@@ -13,9 +58,9 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
const uint8_t n15 = data[index + 0x15];
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
info.type = TypeBsx;
info.mapper = BSXROM;
info.region = NTSC; //BS-X only released in Japan
type = TypeBsx;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return;
}
}
@@ -28,149 +73,153 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
info.type = TypeSufamiTurboBios;
type = TypeSufamiTurboBios;
} else {
info.type = TypeSufamiTurbo;
type = TypeSufamiTurbo;
}
info.mapper = STROM;
info.region = NTSC; //Sufami Turbo only released in Japan
return; //RAM size handled internally by load_cart_st();
mapper = STROM;
region = NTSC; //Sufami Turbo only released in Japan
return; //RAM size handled outside this routine
}
//==========================
//detect Super Game Boy BIOS
//==========================
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
type = TypeSuperGameBoy2Bios;
return;
}
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
type = TypeSuperGameBoy1Bios;
return;
}
//=====================
//detect standard carts
//=====================
const uint8 mapper = data[index + Mapper];
const uint8 rom_type = data[index + RomType];
const uint8 rom_size = data[index + RomSize];
const uint8 company = data[index + Company];
const uint8 region = data[index + CartRegion] & 0x7f;
//detect presence of BS-X flash cartridge connector (reads extended header information)
if(data[index - 14] == 'Z') {
if(data[index - 11] == 'J') {
uint8 n13 = data[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
info.bsx_slot = true;
has_bsx_slot = true;
}
}
}
}
if(info.bsx_slot == true) {
if(has_bsx_slot) {
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
//BS-X base cart
info.type = TypeBsxBios;
info.mapper = BSXROM;
info.region = NTSC; //BS-X only released in Japan
type = TypeBsxBios;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
} else {
info.type = TypeBsxSlotted;
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
type = TypeBsxSlotted;
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
region = NTSC; //BS-X slotted cartridges only released in Japan
}
} else {
//standard cart
info.type = TypeNormal;
type = TypeNormal;
if(index == 0x7fc0 && size >= 0x401000) {
info.mapper = ExLoROM;
} else if(index == 0x7fc0 && mapper == 0x32) {
info.mapper = ExLoROM;
mapper = ExLoROM;
} else if(index == 0x7fc0 && mapperid == 0x32) {
mapper = ExLoROM;
} else if(index == 0x7fc0) {
info.mapper = LoROM;
mapper = LoROM;
} else if(index == 0xffc0) {
info.mapper = HiROM;
mapper = HiROM;
} else { //index == 0x40ffc0
info.mapper = ExHiROM;
mapper = ExHiROM;
}
}
if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
info.superfx = true;
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
has_superfx = true;
mapper = SuperFXROM;
ram_size = 1024 << (data[index - 3] & 7);
if(ram_size == 1024) ram_size = 0;
}
if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) {
info.sa1 = true;
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
has_sa1 = true;
mapper = SA1ROM;
}
if(mapper == 0x35 && rom_type == 0x55) {
info.srtc = true;
if(mapperid == 0x35 && rom_type == 0x55) {
has_srtc = true;
}
if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
info.sdd1 = true;
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
has_sdd1 = true;
}
if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
info.spc7110 = true;
info.spc7110rtc = (rom_type == 0xf9);
info.mapper = SPC7110ROM;
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
has_spc7110 = true;
has_spc7110rtc = (rom_type == 0xf9);
mapper = SPC7110ROM;
}
if(mapper == 0x20 && rom_type == 0xf3) {
info.cx4 = true;
if(mapperid == 0x20 && rom_type == 0xf3) {
has_cx4 = true;
}
if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) {
info.dsp1 = true;
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
has_dsp1 = true;
}
if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) {
info.dsp1 = true;
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
has_dsp1 = true;
}
if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
info.dsp1 = true;
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
has_dsp1 = true;
}
if(info.dsp1 == true) {
if((mapper & 0x2f) == 0x20 && size <= 0x100000) {
info.dsp1_mapper = DSP1LoROM1MB;
} else if((mapper & 0x2f) == 0x20) {
info.dsp1_mapper = DSP1LoROM2MB;
} else if((mapper & 0x2f) == 0x21) {
info.dsp1_mapper = DSP1HiROM;
if(has_dsp1 == true) {
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
dsp1_mapper = DSP1LoROM1MB;
} else if((mapperid & 0x2f) == 0x20) {
dsp1_mapper = DSP1LoROM2MB;
} else if((mapperid & 0x2f) == 0x21) {
dsp1_mapper = DSP1HiROM;
}
}
if(mapper == 0x20 && rom_type == 0x05) {
info.dsp2 = true;
if(mapperid == 0x20 && rom_type == 0x05) {
has_dsp2 = true;
}
if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) {
info.dsp3 = true;
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
has_dsp3 = true;
}
if(mapper == 0x30 && rom_type == 0x03) {
info.dsp4 = true;
if(mapperid == 0x30 && rom_type == 0x03) {
has_dsp4 = true;
}
if(mapper == 0x30 && rom_type == 0x25) {
info.obc1 = true;
if(mapperid == 0x30 && rom_type == 0x25) {
has_obc1 = true;
}
if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
info.st010 = true;
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
has_st010 = true;
}
if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) {
info.st011 = true;
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
has_st011 = true;
}
if(mapper == 0x30 && rom_type == 0xf5) {
info.st018 = true;
if(mapperid == 0x30 && rom_type == 0xf5) {
has_st018 = true;
}
if(data[index + RamSize] & 7) {
info.ram_size = 1024 << (data[index + RamSize] & 7);
} else {
info.ram_size = 0;
}
//0, 1, 13 = NTSC; 2 - 12 = PAL
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
}
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const {

View File

@@ -0,0 +1,37 @@
#ifdef CARTRIDGE_CPP
void Cartridge::serialize(serializer &s) {
if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) {
s.array(memory::cartram.data(), memory::cartram.size());
}
if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) {
s.array(memory::cartrtc.data(), memory::cartrtc.size());
}
if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) {
s.array(memory::bsxram.data(), memory::bsxram.size());
}
if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) {
s.array(memory::bsxpram.data(), memory::bsxpram.size());
}
if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) {
s.array(memory::stAram.data(), memory::stAram.size());
}
if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) {
s.array(memory::stBram.data(), memory::stBram.size());
}
if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) {
s.array(memory::gbram.data(), memory::gbram.size());
}
if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) {
s.array(memory::gbrtc.data(), memory::gbrtc.size());
}
}
#endif

View File

@@ -1,3 +1,3 @@
::@mingw32-make platform=win compiler=mingw32-gcc
@mingw32-make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
@mingw32-make
@pause

View File

@@ -1,2 +0,0 @@
make platform=x compiler=gcc
#make platform=x compiler=gcc enable_gzip=true enable_jma=true

View File

@@ -0,0 +1,2 @@
bool Cheat::active() const { return cheat_enabled; }
bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); }

View File

@@ -1,234 +1,77 @@
#include <../base.hpp>
#define CHEAT_CPP
namespace SNES {
Cheat cheat;
Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) {
enabled = source.enabled;
code = source.code;
desc = source.desc;
count = source.count;
addr.reset();
data.reset();
for(unsigned n = 0; n < count; n++) {
addr[n] = source.addr[n];
data[n] = source.data[n];
bool Cheat::enabled() const {
return system_enabled;
}
return *this;
void Cheat::enable(bool state) {
system_enabled = state;
cheat_enabled = system_enabled && code_enabled;
}
//used to sort cheat code list by description
bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) {
return strcmp(desc, source.desc) < 0;
void Cheat::synchronize() {
memset(bitmask, 0x00, sizeof bitmask);
code_enabled = false;
for(unsigned i = 0; i < size(); i++) {
const CheatCode &code = operator[](i);
if(code.enabled == false) continue;
for(unsigned n = 0; n < code.addr.size(); n++) {
code_enabled = true;
unsigned addr = mirror(code.addr[n]);
bitmask[addr >> 3] |= 1 << (addr & 7);
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
unsigned mirroraddr;
for(unsigned x = 0; x <= 0x3f; x++) {
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
}
}
}
}
//parse item ("0123-4567+89AB-CDEF"), return cheat_t item
//return true if code is valid, false otherwise
bool Cheat::decode(const char *s, Cheat::cheat_t &item) const {
item.enabled = false;
item.count = 0;
lstring list;
list.split("+", s);
for(unsigned n = 0; n < list.size(); n++) {
unsigned addr;
uint8_t data;
type_t type;
if(decode(list[n], addr, data, type) == false) return false;
item.addr[item.count] = addr;
item.data[item.count] = data;
item.count++;
cheat_enabled = system_enabled && code_enabled;
}
return true;
}
bool Cheat::read(unsigned addr, uint8 &data) const {
addr = mirror(addr);
//read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code.
//returns true if cheat code was found, false if it was not.
//when true, cheat code substitution value is stored in data.
bool Cheat::read(unsigned addr, uint8_t &data) const {
addr = mirror_address(addr);
for(unsigned i = 0; i < code.size(); i++) {
if(enabled(i) == false) continue;
for(unsigned i = 0; i < size(); i++) {
const CheatCode &code = operator[](i);
if(code.enabled == false) continue;
for(unsigned n = 0; n < code[i].count; n++) {
if(addr == mirror_address(code[i].addr[n])) {
data = code[i].data[n];
for(unsigned n = 0; n < code.addr.size(); n++) {
if(addr == mirror(code.addr[n])) {
data = code.data[n];
return true;
}
}
}
//code not found, or code is disabled
return false;
}
//==============
//master control
//==============
//global cheat system enable/disable:
//if disabled, *all* cheat codes are disabled;
//otherwise only individually disabled codes are.
bool Cheat::enabled() const {
return cheat_system_enabled;
Cheat::Cheat() {
system_enabled = true;
synchronize();
}
void Cheat::enable() {
cheat_system_enabled = true;
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
}
//===============
//encode / decode
//===============
void Cheat::disable() {
cheat_system_enabled = false;
cheat_enabled = false;
}
//================================
//cheat list manipulation routines
//================================
bool Cheat::add(bool enable, const char *code_, const char *desc_) {
cheat_t item;
if(decode(code_, item) == false) return false;
unsigned i = code.size();
code[i] = item;
code[i].enabled = enable;
code[i].desc = desc_;
code[i].code = code_;
encode_description(code[i].desc);
update(code[i]);
update_cheat_status();
return true;
}
bool Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) {
cheat_t item;
if(decode(code_, item) == false) return false;
//disable current code and clear from code lookup table
code[i].enabled = false;
update(code[i]);
code[i] = item;
code[i].enabled = enable;
code[i].desc = desc_;
code[i].code = code_;
encode_description(code[i].desc);
update(code[i]);
update_cheat_status();
return true;
}
bool Cheat::remove(unsigned i) {
unsigned size = code.size();
if(i >= size) return false; //also verifies size cannot be < 1
for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1];
code.resize(size - 1);
update_cheat_status();
return true;
}
bool Cheat::get(unsigned i, cheat_t &item) const {
if(i >= code.size()) return false;
item = code[i];
decode_description(item.desc);
return true;
}
//==============================
//cheat status modifier routines
//==============================
bool Cheat::enabled(unsigned i) const {
return (i < code.size() ? code[i].enabled : false);
}
void Cheat::enable(unsigned i) {
if(i >= code.size()) return;
code[i].enabled = true;
update(code[i]);
update_cheat_status();
}
void Cheat::disable(unsigned i) {
if(i >= code.size()) return;
code[i].enabled = false;
update(code[i]);
update_cheat_status();
}
//===============================
//cheat file load / save routines
//
//file format:
//"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n
//...
//===============================
bool Cheat::load(const char *fn) {
string data;
if(!data.readfile(fn)) return false;
data.replace("\r\n", "\n");
data.qreplace(" ", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
lstring part;
part.qsplit(",", line[i]);
if(part.size() != 3) continue;
trim(part[0], "\"");
add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]);
}
return true;
}
bool Cheat::save(const char *fn) const {
file fp;
if(!fp.open(fn, file::mode_write)) return false;
for(unsigned i = 0; i < code.size(); i++) {
fp.print(string()
<< "\"" << code[i].desc << "\", "
<< (code[i].enabled ? "enabled, " : "disabled, ")
<< code[i].code << "\r\n");
}
fp.close();
return true;
}
void Cheat::clear() {
cheat_enabled_code_exists = false;
memset(mask, 0, 0x200000);
code.reset();
}
Cheat::Cheat() : cheat_system_enabled(true) {
clear();
}
//==================
//internal functions
//==================
//string <> binary code translation routines
//decode() "7e123456" -> 0x7e123456
//encode() 0x7e123456 -> "7e123456"
bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const {
bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
string t = s;
strlower(t);
@@ -274,9 +117,11 @@ bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) c
} else {
return false;
}
#undef ischr
}
bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) {
char t[16];
if(type == ProActionReplay) {
@@ -306,87 +151,46 @@ bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
}
}
//speed up S-CPU memory reads by disabling cheat code lookup when either:
//a) cheat system is disabled by user, or b) no enabled cheat codes exist
void Cheat::update_cheat_status() {
for(unsigned i = 0; i < code.size(); i++) {
if(code[i].enabled) {
cheat_enabled_code_exists = true;
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
return;
}
}
cheat_enabled_code_exists = false;
cheat_enabled = false;
//========
//internal
//========
unsigned Cheat::mirror(unsigned addr) const {
//$00-3f|80-bf:0000-1fff -> $7e:0000-1fff
if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff));
return addr;
}
//address lookup table manipulation and mirroring
//mirror_address() 0x000000 -> 0x7e0000
//set() enable specified address, mirror accordingly
//clear() disable specified address, mirror accordingly
unsigned Cheat::mirror_address(unsigned addr) const {
if((addr & 0x40e000) != 0x0000) return addr;
//8k WRAM mirror
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
return (0x7e0000 + (addr & 0x1fff));
//=========
//CheatCode
//=========
bool CheatCode::operator=(string s) {
addr.reset();
data.reset();
lstring list;
list.split("+", s.replace(" ", ""));
for(unsigned i = 0; i < list.size(); i++) {
unsigned addr_;
uint8 data_;
Cheat::Type type_;
if(Cheat::decode(list[i], addr_, data_, type_) == false) {
addr.reset();
data.reset();
return false;
}
//updates mask[] table enabled bits;
//must be called after modifying item.enabled state.
void Cheat::update(const cheat_t &item) {
for(unsigned n = 0; n < item.count; n++) {
(item.enabled) ? set(item.addr[n]) : clear(item.addr[n]);
}
addr.add(addr_);
data.add(data_);
}
void Cheat::set(unsigned addr) {
addr = mirror_address(addr);
mask[addr >> 3] |= 1 << (addr & 7);
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
unsigned mirror;
for(unsigned x = 0; x <= 0x3f; x++) {
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] |= 1 << (mirror & 7);
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] |= 1 << (mirror & 7);
}
}
return true;
}
void Cheat::clear(unsigned addr) {
addr = mirror_address(addr);
//if there is more than one cheat code using the same address,
//(eg with a different override value) then do not clear code
//lookup table entry.
uint8_t r;
if(read(addr, r) == true) return;
mask[addr >> 3] &= ~(1 << (addr & 7));
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
unsigned mirror;
for(unsigned x = 0; x <= 0x3f; x++) {
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] &= ~(1 << (mirror & 7));
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] &= ~(1 << (mirror & 7));
}
}
CheatCode::CheatCode() {
enabled = false;
}
//these two functions are used to safely store description text inside .cfg file format.
string& Cheat::encode_description(string &desc) const {
desc.replace("\"", "\\q");
desc.replace("\n", "\\n");
return desc;
}
string& Cheat::decode_description(string &desc) const {
desc.replace("\\q", "\"");
desc.replace("\\n", "\n");
return desc;
}

View File

@@ -1,69 +1,35 @@
class Cheat {
public:
enum type_t {
ProActionReplay,
GameGenie,
};
struct cheat_t {
struct CheatCode {
bool enabled;
string code;
string desc;
unsigned count;
array<unsigned> addr;
array<uint8_t> data;
array<uint8> data;
cheat_t& operator=(const cheat_t&);
bool operator<(const cheat_t&);
bool operator=(string);
CheatCode();
};
bool decode(const char *s, cheat_t &item) const;
bool read(unsigned addr, uint8_t &data) const;
class Cheat : public vector<CheatCode> {
public:
enum Type { ProActionReplay, GameGenie };
bool enabled() const;
void enable();
void disable();
void enable(bool);
void synchronize();
bool read(unsigned, uint8&) const;
inline unsigned count() const { return code.size(); }
inline bool active() const { return cheat_enabled; }
inline bool exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); }
bool add(bool enable, const char *code, const char *desc);
bool edit(unsigned i, bool enable, const char *code, const char *desc);
bool remove(unsigned i);
bool get(unsigned i, cheat_t &item) const;
bool enabled(unsigned i) const;
void enable(unsigned i);
void disable(unsigned i);
bool load(const char *fn);
bool save(const char *fn) const;
void clear();
inline bool active() const;
inline bool exists(unsigned addr) const;
Cheat();
static bool decode(const char*, unsigned&, uint8&, Type&);
static bool encode(string&, unsigned, uint8, Type);
private:
bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled);
bool cheat_enabled_code_exists;
bool cheat_system_enabled;
uint8_t mask[0x200000];
vector<cheat_t> code;
bool decode(const char *str, unsigned &addr, uint8_t &data, type_t &type) const;
bool encode(string &str, unsigned addr, uint8_t data, type_t type) const;
void update_cheat_status();
unsigned mirror_address(unsigned addr) const;
void update(const cheat_t& item);
void set(unsigned addr);
void clear(unsigned addr);
string& encode_description(string &desc) const;
string& decode_description(string &desc) const;
uint8 bitmask[0x200000];
bool system_enabled;
bool code_enabled;
bool cheat_enabled;
unsigned mirror(unsigned) const;
};
extern Cheat cheat;

195
src/chip/21fx/21fx.cpp Normal file
View File

@@ -0,0 +1,195 @@
#include <../base.hpp>
//B-bus interface
//$21f0 command port (r/w)
//-------------------------
//$00 set data port address (sr[3-0] = address)
//$01 set audio track number (sr[1-0] = track number)
//$02 set volume (sr[1] = left, sr[0] = right)
//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat)
//
//d7 = data port busy
//d6 = audio port busy
//d5 = audio playing
//d4 = reserved (0)
//d3-d0 = version (0)
//
//
//$21f1 parameter port (w)
//-------------------------
//(shift register)
//
//
//$21f2 data port (r)
//--------------------
//(auto-increment read port)
//A-bus interface
//$2200 command port (r/w)
//-------------------------
//$00 set data port address (sr[3-0] = address)
//$01 set audio track number (sr[1-0] = track number)
//$02 set volume (sr[1] = left, sr[0] = right)
//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat)
//
//d7 = data port busy
//d6 = audio port busy
//d5 = audio playing
//d4 = reserved (0)
//d3-d0 = version (0)
//
//$2201 data port (r/w)
//----------------------
//(shift register)
//
//(auto-increment read port)
#define S21FX_CPP
namespace SNES {
S21fx s21fx;
#include "serialization.cpp"
void S21fx::enter() {
scheduler.clock.cop_freq = 44100;
while(true) {
if(scheduler.sync == Scheduler::SyncAll) {
scheduler.exit(Scheduler::SynchronizeEvent);
}
int16 left = 0, right = 0;
if((mmio.status & AudioPlaying) && !mmio.audio_pause) {
if(audiofile.open()) {
if(audiofile.end()) {
if(!mmio.audio_repeat) mmio.status &= ~AudioPlaying;
audiofile.seek(mmio.audio_offset = 58);
} else {
mmio.audio_offset += 4;
left = audiofile.readl(2);
right = audiofile.readl(2);
}
} else {
mmio.status &= ~AudioPlaying;
}
}
left = sclamp<16>((double)left * (double)mmio.audio_volume_left / 255.0);
right = sclamp<16>((double)right * (double)mmio.audio_volume_right / 255.0);
audio.coprocessor_sample(left, right);
scheduler.addclocks_cop(1);
scheduler.sync_copcpu();
}
}
void S21fx::init() {
}
void S21fx::enable() {
audio.coprocessor_enable(true);
audio.coprocessor_frequency(44100.0);
for(unsigned i = 0x21f0; i <= 0x21f7; i++) {
memory::mmio.map(i, *this);
}
memory::mmio.map(0x2200, *this);
memory::mmio.map(0x2201, *this);
if(datafile.open()) datafile.close();
datafile.open(string() << basepath << "21fx.bin", file::mode_read);
}
void S21fx::power() {
reset();
}
void S21fx::reset() {
mmio.status = DataPortBusy | AudioBusy;
mmio.shift_register = 0;
mmio.data_offset = 0;
mmio.audio_offset = 0;
mmio.audio_track = 0;
mmio.audio_volume_left = 255;
mmio.audio_volume_right = 255;
mmio.audio_repeat = false;
mmio.audio_pause = false;
}
uint8 S21fx::mmio_read(unsigned addr) {
addr &= 0xffff;
if((addr == 0x21f0) || (addr == 0x2200)) {
return mmio.status | 0x00;
}
if((addr == 0x21f2) || (addr == 0x2201)) {
if(mmio.status & DataPortBusy) return 0x00;
mmio.data_offset++;
if(datafile.open()) return datafile.read();
return 0x00;
}
return 0x00;
}
void S21fx::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if((addr == 0x21f0) || (addr == 0x2200)) {
if(data == 0x00) {
mmio.data_offset = mmio.shift_register & 0xffffffff;
if(datafile.open()) {
datafile.seek(mmio.data_offset);
}
mmio.status &= ~DataPortBusy;
}
if(data == 0x01) {
mmio.audio_track = mmio.shift_register & 0xffff;
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "%.5u", mmio.audio_track);
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
audiofile.seek(mmio.audio_offset = 58); //skip WAV header
}
mmio.status &= ~(AudioBusy | AudioPlaying);
}
if(data == 0x02) {
mmio.audio_volume_left = mmio.shift_register >> 8;
mmio.audio_volume_right = mmio.shift_register >> 0;
}
if(data == 0x03) {
mmio.status |= AudioPlaying;
mmio.audio_repeat = mmio.shift_register & 1;
mmio.audio_pause = mmio.shift_register & 2;
}
mmio.shift_register = 0;
}
if((addr == 0x21f1) || (addr == 0x2201)) {
mmio.shift_register = (mmio.shift_register << 8) | data;
}
}
void S21fx::base(const string& path) {
basepath = path;
}
bool S21fx::exists() {
return file::exists(string() << basepath << "21fx.bin");
}
S21fx::S21fx() {
}
}

44
src/chip/21fx/21fx.hpp Normal file
View File

@@ -0,0 +1,44 @@
class S21fx : public MMIO {
public:
void enter();
void init();
void enable();
void power();
void reset();
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
void base(const string &path);
bool exists();
void serialize(serializer&);
S21fx();
private:
string basepath;
file datafile;
file audiofile;
enum Flag {
DataPortBusy = 0x80,
AudioBusy = 0x40,
AudioPlaying = 0x20,
};
struct MMIO {
uint8 status;
uint64 shift_register;
uint32 data_offset;
uint32 audio_offset;
uint16 audio_track;
uint8 audio_volume_left;
uint8 audio_volume_right;
bool audio_repeat;
bool audio_pause;
} mmio;
};
extern S21fx s21fx;

View File

@@ -0,0 +1,31 @@
#ifdef S21FX_CPP
void S21fx::serialize(serializer &s) {
s.integer(mmio.status);
s.integer(mmio.shift_register);
s.integer(mmio.data_offset);
s.integer(mmio.audio_offset);
s.integer(mmio.audio_track);
s.integer(mmio.audio_volume_left);
s.integer(mmio.audio_volume_right);
s.integer(mmio.audio_repeat);
s.integer(mmio.audio_pause);
//flush file handles and indices, as a different track may be playing,
//or the file offsets may be at the wrong location ...
if(datafile.open()) datafile.close();
if(datafile.open(string() << basepath << "21fx.bin", file::mode_read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "%.5u", mmio.audio_track);
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
audiofile.seek(mmio.audio_offset);
}
}
#endif

View File

@@ -1,8 +1,10 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#define BSX_CPP
#include "bsx.hpp"
#define BSX_CPP
namespace SNES {
#include "bsx_base.cpp"
#include "bsx_cart.cpp"
#include "bsx_flash.cpp"
};

View File

@@ -32,16 +32,10 @@ public:
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
MappedRAM sram;
MappedRAM psram;
BSXCart();
~BSXCart();
private:
uint8 *sram_data; //256kbit SRAM
uint8 *psram_data; // 4mbit PSRAM
struct {
uint8 r[16];
} regs;

View File

@@ -1,5 +1,7 @@
#ifdef BSX_CPP
BSXBase bsxbase;
void BSXBase::init() {
}
@@ -135,3 +137,4 @@ void BSXBase::mmio_write(unsigned addr, uint8 data) {
}
#endif

View File

@@ -1,5 +1,7 @@
#ifdef BSX_CPP
BSXCart bsxcart;
void BSXCart::init() {
}
@@ -20,7 +22,7 @@ void BSXCart::reset() {
}
void BSXCart::update_memory_map() {
Memory &cart = (regs.r[0x01] & 0x80) == 0x00 ? (Memory&)bsxflash : (Memory&)psram;
Memory &cart = (regs.r[0x01] & 0x80) == 0x00 ? (Memory&)bsxflash : (Memory&)memory::bsxpram;
if((regs.r[0x02] & 0x80) == 0x00) {
//LoROM mapping
@@ -35,16 +37,16 @@ void BSXCart::update_memory_map() {
}
if(regs.r[0x03] & 0x80) {
bus.map(Bus::MapLinear, 0x60, 0x6f, 0x0000, 0xffff, psram);
//bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, psram);
bus.map(Bus::MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bsxpram);
//bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram);
}
if((regs.r[0x05] & 0x80) == 0x00) {
bus.map(Bus::MapLinear, 0x40, 0x4f, 0x0000, 0xffff, psram);
bus.map(Bus::MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::bsxpram);
}
if((regs.r[0x06] & 0x80) == 0x00) {
bus.map(Bus::MapLinear, 0x50, 0x5f, 0x0000, 0xffff, psram);
bus.map(Bus::MapLinear, 0x50, 0x5f, 0x0000, 0xffff, memory::bsxpram);
}
if(regs.r[0x07] & 0x80) {
@@ -55,8 +57,8 @@ void BSXCart::update_memory_map() {
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom);
}
bus.map(Bus::MapShadow, 0x20, 0x3f, 0x6000, 0x7fff, psram);
bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, psram);
bus.map(Bus::MapShadow, 0x20, 0x3f, 0x6000, 0x7fff, memory::bsxpram);
bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram);
}
uint8 BSXCart::mmio_read(unsigned addr) {
@@ -66,7 +68,7 @@ uint8 BSXCart::mmio_read(unsigned addr) {
}
if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM
return sram.read(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff));
return memory::bsxram.read(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff));
}
return 0x00;
@@ -81,21 +83,15 @@ void BSXCart::mmio_write(unsigned addr, uint8 data) {
}
if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM
return sram.write(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data);
return memory::bsxram.write(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data);
}
}
BSXCart::BSXCart() {
sram_data = new uint8_t[ 32 * 1024];
psram_data = new uint8_t[512 * 1024];
sram.map (sram_data, 32 * 1024);
psram.map(psram_data, 512 * 1024);
}
BSXCart::~BSXCart() {
delete[] sram_data;
delete[] psram_data;
}
#endif

View File

@@ -1,5 +1,7 @@
#ifdef BSX_CPP
BSXFlash bsxflash;
void BSXFlash::init() {}
void BSXFlash::enable() {}
@@ -15,10 +17,11 @@ void BSXFlash::reset() {
regs.flash_enable = false;
regs.read_enable = false;
regs.write_enable = false;
memory::bsxflash.write_protect(!regs.write_enable);
}
unsigned BSXFlash::size() const {
return memory::bscram.size();
return memory::bsxflash.size();
}
uint8 BSXFlash::read(unsigned addr) {
@@ -45,7 +48,7 @@ uint8 BSXFlash::read(unsigned addr) {
}
}
return memory::bscram.read(addr);
return memory::bsxflash.read(addr);
}
void BSXFlash::write(unsigned addr, uint8 data) {
@@ -64,11 +67,11 @@ void BSXFlash::write(unsigned addr, uint8 data) {
regs.write_new = data;
if(regs.write_enable && regs.write_old == regs.write_new) {
return memory::bscram.write(addr, data);
return memory::bsxflash.write(addr, data);
}
} else {
if(regs.write_enable) {
return memory::bscram.write(addr, data);
return memory::bsxflash.write(addr, data);
}
}
@@ -107,7 +110,10 @@ void BSXFlash::write(unsigned addr, uint8 data) {
regs.read_enable = false;
regs.write_enable = false;
}
memory::bsxflash.write_protect(!regs.write_enable);
}
}
#endif

View File

@@ -1,3 +1,6 @@
#include "supergameboy/supergameboy.hpp"
#include "superfx/superfx.hpp"
#include "sa1/sa1.hpp"
#include "bsx/bsx.hpp"
#include "srtc/srtc.hpp"
#include "sdd1/sdd1.hpp"
@@ -9,3 +12,6 @@
#include "dsp4/dsp4.hpp"
#include "obc1/obc1.hpp"
#include "st010/st010.hpp"
#include "st011/st011.hpp"
#include "st018/st018.hpp"
#include "21fx/21fx.hpp"

View File

@@ -1,30 +1,40 @@
/*
C4 emulation
Used in Rockman X2/X3 (Megaman X2/X3)
Portions (c) anomie, Overload, zsKnight, Nach, byuu
*/
//=============
//Cx4 emulation
//=============
//Used in Rockman X2/X3 (Megaman X2/X3)
//Portions (c) anomie, Overload, zsKnight, Nach, byuu
#include <../base.hpp>
#define CX4_CPP
namespace SNES {
#include "cx4.hpp"
#include "cx4data.cpp"
#include "cx4fn.cpp"
#include "cx4oam.cpp"
#include "cx4ops.cpp"
Cx4 cx4;
void Cx4::init() {}
void Cx4::enable() {}
#include "serialization.cpp"
#include "data.cpp"
#include "functions.cpp"
#include "oam.cpp"
#include "opcodes.cpp"
void Cx4::init() {
}
void Cx4::enable() {
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
}
uint32 Cx4::ldr(uint8 r) {
uint16 addr = 0x0080 + (r * 3);
return (reg[addr]) | (reg[addr + 1] << 8) | (reg[addr + 2] << 16);
return (reg[addr + 0] << 0)
| (reg[addr + 1] << 8)
| (reg[addr + 2] << 16);
}
void Cx4::str(uint8 r, uint32 data) {
uint16 addr = 0x0080 + (r * 3);
reg[addr ] = (data);
reg[addr + 0] = (data >> 0);
reg[addr + 1] = (data >> 8);
reg[addr + 2] = (data >> 16);
}
@@ -70,6 +80,7 @@ void Cx4::immediate_reg(uint32 start) {
void Cx4::transfer_data() {
uint32 src;
uint16 dest, count;
src = (reg[0x40]) | (reg[0x41] << 8) | (reg[0x42] << 16);
count = (reg[0x43]) | (reg[0x44] << 8);
dest = (reg[0x45]) | (reg[0x46] << 8);
@@ -151,12 +162,12 @@ void Cx4::writeb(uint16 addr, uint8 data) {
}
void Cx4::writew(uint16 addr, uint16 data) {
write(addr, data);
write(addr + 0, data >> 0);
write(addr + 1, data >> 8);
}
void Cx4::writel(uint16 addr, uint32 data) {
write(addr, data);
write(addr + 0, data >> 0);
write(addr + 1, data >> 8);
write(addr + 2, data >> 16);
}
@@ -195,3 +206,5 @@ void Cx4::reset() {
memset(ram, 0, 0x0c00);
memset(reg, 0, 0x0100);
}
};

View File

@@ -1,4 +1,15 @@
class Cx4 : public Memory {
public:
void init();
void enable();
void power();
void reset();
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
private:
uint8 ram[0x0c00];
uint8 reg[0x0100];
@@ -15,9 +26,6 @@ private:
int16 C4WFXVal, C4WFYVal, C4WFZVal, C4WFX2Val, C4WFY2Val, C4WFDist, C4WFScale;
int16 C41FXVal, C41FYVal, C41FAngleRes, C41FDist, C41FDistVal;
double tanval;
double c4x,c4y,c4z, c4x2,c4y2,c4z2;
void C4TransfWireFrame();
void C4TransfWireFrame2();
void C4CalcWireFrame();
@@ -82,16 +90,6 @@ public:
void writeb(uint16 addr, uint8 data);
void writew(uint16 addr, uint16 data);
void writel(uint16 addr, uint32 data);
//
void init();
void enable();
void power();
void reset();
uint8 read (unsigned addr);
void write(unsigned addr, uint8 data);
};
extern Cx4 cx4;

View File

@@ -10,9 +10,10 @@
//Wireframe Helpers
void Cx4::C4TransfWireFrame() {
c4x = (double)C4WFXVal;
c4y = (double)C4WFYVal;
c4z = (double)C4WFZVal - 0x95;
double c4x = (double)C4WFXVal;
double c4y = (double)C4WFYVal;
double c4z = (double)C4WFZVal - 0x95;
double tanval, c4x2, c4y2, c4z2;
//Rotate X
tanval = -(double)C4WFX2Val * PI * 2 / 128;
@@ -52,9 +53,10 @@ void Cx4::C4CalcWireFrame() {
}
void Cx4::C4TransfWireFrame2() {
c4x = (double)C4WFXVal;
c4y = (double)C4WFYVal;
c4z = (double)C4WFZVal;
double c4x = (double)C4WFXVal;
double c4y = (double)C4WFYVal;
double c4z = (double)C4WFZVal;
double tanval, c4x2, c4y2, c4z2;
//Rotate X
tanval = -(double)C4WFX2Val * PI * 2 / 128;
@@ -82,6 +84,7 @@ uint32 point1, point2;
int16 X1, Y1, Z1;
int16 X2, Y2, Z2;
uint8 Color;
for(int32 i = ram[0x0295]; i > 0; i--, line += 5) {
if(bus.read(line) == 0xff && bus.read(line + 1) == 0xff) {
int32 tmp = line - 5;
@@ -139,8 +142,8 @@ void Cx4::C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2,
uint8 bit = 0x80 >> ((X1 >> 8) & 7);
ram[addr + 0x300] &= ~bit;
ram[addr + 0x301] &= ~bit;
if(Color & 1) { ram[addr + 0x300] |= bit; }
if(Color & 2) { ram[addr + 0x301] |= bit; }
if(Color & 1) ram[addr + 0x300] |= bit;
if(Color & 2) ram[addr + 0x301] |= bit;
}
X1 += X2;
Y1 += Y2;
@@ -153,6 +156,7 @@ int16 A, B, C, D;
//Calculate matrix
int32 XScale = readw(0x1f8f);
int32 YScale = readw(0x1f92);
if(XScale & 0x8000)XScale = 0x7fff;
if(YScale & 0x8000)YScale = 0x7fff;
@@ -205,6 +209,7 @@ uint32 X, Y;
uint8 byte;
int32 outidx = 0;
uint8 bit = 0x80;
for(int32 y = 0; y < h; y++) {
X = LineX;
Y = LineY;
@@ -218,10 +223,10 @@ uint8 bit = 0x80;
}
//De-bitplanify
if(byte & 1) { ram[outidx ] |= bit; }
if(byte & 2) { ram[outidx + 1] |= bit; }
if(byte & 4) { ram[outidx + 16] |= bit; }
if(byte & 8) { ram[outidx + 17] |= bit; }
if(byte & 1) ram[outidx ] |= bit;
if(byte & 2) ram[outidx + 1] |= bit;
if(byte & 4) ram[outidx + 16] |= bit;
if(byte & 8) ram[outidx + 17] |= bit;
bit >>= 1;
if(!bit) {

View File

@@ -13,6 +13,7 @@ uint32 oamptr2;
int16 sprx, spry;
uint8 sprname, sprattr;
uint8 sprcount;
globalx = readw(0x621);
globaly = readw(0x623);
oamptr2 = 0x200 + (ram[0x626] >> 2);
@@ -22,6 +23,7 @@ uint8 sprcount;
sprcount = 128 - ram[0x626];
uint8 offset = (ram[0x626] & 3) * 2;
uint32 srcptr = 0x220;
for(int i = ram[0x620]; i > 0 && sprcount > 0; i--, srcptr += 16) {
sprx = readw(srcptr) - globalx;
spry = readw(srcptr + 2) - globaly;
@@ -108,6 +110,7 @@ uint32 ptr = 0;
ptr = 0xb02;
uint32 ptr2 = 0;
for(int32 i = readw(0xb00); i > 0; i--, ptr += 2, ptr2 += 8) {
C4WFXVal = readw((read(ptr + 0) << 4) + 1);
C4WFYVal = readw((read(ptr + 0) << 4) + 5);
@@ -139,6 +142,7 @@ uint32 x, y;
int32 scalex, scaley;
int32 cx, cy;
int32 i, j;
width = read(0x1f89);
height = read(0x1f8c);
cx = readw(0x1f80);
@@ -160,6 +164,7 @@ int32 i, j;
uint8 pixel = (j & 1) ? (ram[srcptr] >> 4) : (ram[srcptr]);
int32 index = (y >> 11) * width * 4 + (x >> 11) * 32 + ((y >> 8) & 7) * 2;
uint8 mask = 0x80 >> ((x >> 8) & 7);
if(pixel & 1) ram[index ] |= mask;
if(pixel & 2) ram[index + 1] |= mask;
if(pixel & 4) ram[index + 16] |= mask;

View File

@@ -33,7 +33,7 @@ void Cx4::op0d() {
C41FXVal = readw(0x1f80);
C41FYVal = readw(0x1f83);
C41FDistVal = readw(0x1f86);
tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal));
double tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal));
tanval = (double)C41FDistVal / tanval;
C41FYVal = (int16)(((double)C41FYVal * tanval) * 0.99);
C41FXVal = (int16)(((double)C41FXVal * tanval) * 0.98);
@@ -103,7 +103,7 @@ void Cx4::op1f() {
if(!C41FXVal) {
C41FAngleRes = (C41FYVal > 0) ? 0x080 : 0x180;
} else {
tanval = ((double)C41FYVal) / ((double)C41FXVal);
double tanval = ((double)C41FYVal) / ((double)C41FXVal);
C41FAngleRes = (short)(atan(tanval) / (PI * 2) * 512);
C41FAngleRes = C41FAngleRes;
if(C41FXVal < 0) {
@@ -122,6 +122,7 @@ int32 tan1 = Tan(angle1);
int32 tan2 = Tan(angle2);
int16 y = readw(0x1f83) - readw(0x1f89);
int16 left, right;
for(int32 j = 0; j < 225; j++, y++) {
if(y >= 0) {
left = sar((int32)tan1 * y, 16) - readw(0x1f80) + readw(0x1f86);

View File

@@ -0,0 +1,39 @@
#ifdef CX4_CPP
void Cx4::serialize(serializer &s) {
s.array(ram);
s.array(reg);
s.integer(r0);
s.integer(r1);
s.integer(r2);
s.integer(r3);
s.integer(r4);
s.integer(r5);
s.integer(r6);
s.integer(r7);
s.integer(r8);
s.integer(r9);
s.integer(r10);
s.integer(r11);
s.integer(r12);
s.integer(r13);
s.integer(r14);
s.integer(r15);
s.integer(C4WFXVal);
s.integer(C4WFYVal);
s.integer(C4WFZVal);
s.integer(C4WFX2Val);
s.integer(C4WFY2Val);
s.integer(C4WFDist);
s.integer(C4WFScale);
s.integer(C41FXVal);
s.integer(C41FYVal);
s.integer(C41FAngleRes);
s.integer(C41FDist);
s.integer(C41FDistVal);
}
#endif

View File

@@ -1,12 +1,34 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#define DSP1_CPP
#include "dsp1.hpp"
#define DSP1_CPP
namespace SNES {
DSP1 dsp1;
#include "serialization.cpp"
#include "dsp1emu.cpp"
void DSP1::init() {}
void DSP1::enable() {}
void DSP1::init() {
}
void DSP1::enable() {
switch(cartridge.dsp1_mapper()) {
case Cartridge::DSP1LoROM1MB: {
bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xffff, *this);
bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, *this);
} break;
case Cartridge::DSP1LoROM2MB: {
bus.map(Bus::MapDirect, 0x60, 0x6f, 0x0000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0xe0, 0xef, 0x0000, 0x7fff, *this);
} break;
case Cartridge::DSP1HiROM: {
bus.map(Bus::MapDirect, 0x00, 0x1f, 0x6000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0x80, 0x9f, 0x6000, 0x7fff, *this);
} break;
}
}
void DSP1::power() {
reset();
@@ -57,3 +79,5 @@ void DSP1::write(unsigned addr, uint8 data) {
dsp1.setDr(data);
}
}
};

View File

@@ -1,10 +1,6 @@
#include "dsp1emu.hpp"
class DSP1 : public Memory {
private:
Dsp1 dsp1;
bool addr_decode(uint16 addr);
public:
void init();
void enable();
@@ -13,6 +9,12 @@ public:
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
private:
Dsp1 dsp1;
bool addr_decode(uint16 addr);
};
extern DSP1 dsp1;

View File

@@ -33,6 +33,8 @@ class Dsp1
void setDr(uint8 iDr);
void reset();
void serialize(serializer&);
private:
enum FsmMajorState {WAIT_COMMAND, READ_DATA, WRITE_DATA};
enum MaxDataAccesses {MAX_READS=7, MAX_WRITES=1024};
@@ -72,7 +74,7 @@ class Dsp1
uint8 mSr; // status register
int mSrLowByteAccess;
uint16 mDr; // "internal" representation of the data register
FsmMajorState mFsmMajorState; // current major state of the FSM
unsigned mFsmMajorState; // current major state of the FSM
uint8 mCommand; // current command processed by the FSM
uint8 mDataCounter; // #uint16 read/writes counter used by the FSM
int16 mReadBuffer[MAX_READS];

View File

@@ -0,0 +1,56 @@
#ifdef DSP1_CPP
void DSP1::serialize(serializer &s) {
dsp1.serialize(s);
}
void Dsp1::serialize(serializer &s) {
for(unsigned i = 0; i < 3; i++) {
s.array(shared.MatrixA[i]);
s.array(shared.MatrixB[i]);
s.array(shared.MatrixC[i]);
}
s.integer(shared.CentreX);
s.integer(shared.CentreY);
s.integer(shared.CentreZ);
s.integer(shared.CentreZ_C);
s.integer(shared.CentreZ_E);
s.integer(shared.VOffset);
s.integer(shared.Les);
s.integer(shared.C_Les);
s.integer(shared.E_Les);
s.integer(shared.SinAas);
s.integer(shared.CosAas);
s.integer(shared.SinAzs);
s.integer(shared.CosAzs);
s.integer(shared.SinAZS);
s.integer(shared.CosAZS);
s.integer(shared.SecAZS_C1);
s.integer(shared.SecAZS_E1);
s.integer(shared.SecAZS_C2);
s.integer(shared.SecAZS_E2);
s.integer(shared.Nx);
s.integer(shared.Ny);
s.integer(shared.Nz);
s.integer(shared.Gx);
s.integer(shared.Gy);
s.integer(shared.Gz);
s.integer(shared.Hx);
s.integer(shared.Hy);
s.integer(shared.Vx);
s.integer(shared.Vy);
s.integer(shared.Vz);
s.integer(mSr);
s.integer(mSrLowByteAccess);
s.integer(mDr);
s.integer(mFsmMajorState);
s.integer(mCommand);
s.integer(mDataCounter);
s.array(mReadBuffer);
s.array(mWriteBuffer);
s.integer(mFreeze);
}
#endif

View File

@@ -1,11 +1,22 @@
#include <../base.hpp>
#define DSP2_CPP
namespace SNES {
#include "dsp2.hpp"
#include "dsp2_op.cpp"
DSP2 dsp2;
void DSP2::init() {}
void DSP2::enable() {}
#include "serialization.cpp"
#include "opcodes.cpp"
void DSP2::init() {
}
void DSP2::enable() {
bus.map(Bus::MapDirect, 0x20, 0x3f, 0x6000, 0x6fff, *this);
bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xbfff, *this);
bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x6000, 0x6fff, *this);
bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xbfff, *this);
}
void DSP2::power() {
reset();
@@ -134,3 +145,5 @@ void DSP2::write(unsigned addr, uint8 data) {
DSP2::DSP2() {}
DSP2::~DSP2() {}
};

View File

@@ -29,6 +29,7 @@ public:
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
DSP2();
~DSP2();

View File

@@ -0,0 +1,26 @@
#ifdef DSP2_CPP
void DSP2::serialize(serializer &s) {
s.integer(status.waiting_for_command);
s.integer(status.command);
s.integer(status.in_count);
s.integer(status.in_index);
s.integer(status.out_count);
s.integer(status.out_index);
s.array(status.parameters);
s.array(status.output);
s.integer(status.op05transparent);
s.integer(status.op05haslen);
s.integer(status.op05len);
s.integer(status.op06haslen);
s.integer(status.op06len);
s.integer(status.op09word1);
s.integer(status.op09word2);
s.integer(status.op0dhaslen);
s.integer(status.op0doutlen);
s.integer(status.op0dinlen);
}
#endif

View File

@@ -1,7 +1,10 @@
#include <../base.hpp>
#define DSP3_CPP
#include "dsp3.hpp"
#define DSP3_CPP
namespace SNES {
DSP3 dsp3;
namespace DSP3i {
#define bool8 uint8
#include "dsp3emu.c"
@@ -12,6 +15,8 @@ void DSP3::init() {
}
void DSP3::enable() {
bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xffff, *this);
bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, *this);
}
void DSP3::power() {
@@ -33,3 +38,5 @@ void DSP3::write(unsigned addr, uint8 data) {
DSP3i::dsp3_byte = data;
DSP3i::DSP3SetByte();
}
};

View File

@@ -1,7 +1,18 @@
#include <../base.hpp>
#define DSP4_CPP
#include "dsp4.hpp"
#define DSP4_CPP
namespace SNES {
DSP4 dsp4;
void DSP4::init() {
}
void DSP4::enable() {
bus.map(Bus::MapDirect, 0x30, 0x3f, 0x8000, 0xffff, *this);
bus.map(Bus::MapDirect, 0xb0, 0xbf, 0x8000, 0xffff, *this);
}
namespace DSP4i {
inline uint16 READ_WORD(uint8 *addr) {
return (addr[0]) + (addr[1] << 8);
@@ -21,12 +32,6 @@ namespace DSP4i {
#undef bool8
};
void DSP4::init() {
}
void DSP4::enable() {
}
void DSP4::power() {
reset();
}
@@ -53,3 +58,5 @@ void DSP4::write(unsigned addr, uint8 data) {
DSP4i::DSP4SetByte();
}
}
};

View File

@@ -2062,9 +2062,9 @@ void DSP4SetByte()
// unknown
case 0x000A:
{
//int16 in1a = DSP4_READ_WORD();
int16 in1a = DSP4_READ_WORD();
int16 in2a = DSP4_READ_WORD();
//int16 in3a = DSP4_READ_WORD();
int16 in3a = DSP4_READ_WORD();
int16 out1a, out2a, out3a, out4a;
DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);

View File

@@ -1,9 +1,19 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#include "obc1.hpp"
void OBC1::init() {}
void OBC1::enable() {}
#define OBC1_CPP
namespace SNES {
OBC1 obc1;
#include "serialization.cpp"
void OBC1::init() {
}
void OBC1::enable() {
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
}
void OBC1::power() {
reset();
@@ -70,3 +80,5 @@ void OBC1::ram_write(unsigned addr, uint8 data) {
OBC1::OBC1() {}
OBC1::~OBC1() {}
};

View File

@@ -8,6 +8,7 @@ public:
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
OBC1();
~OBC1();

View File

@@ -0,0 +1,9 @@
#ifdef OBC1_CPP
void OBC1::serialize(serializer &s) {
s.integer(status.address);
s.integer(status.baseptr);
s.integer(status.shift);
}
#endif

243
src/chip/sa1/bus/bus.cpp Normal file
View File

@@ -0,0 +1,243 @@
#ifdef SA1_CPP
VBRBus vbrbus;
SA1Bus sa1bus;
namespace memory {
StaticRAM iram(2048);
//accessed by:
VectorSelectionPage vectorsp; //S-CPU + SA-1
CPUIRAM cpuiram; //S-CPU
SA1IRAM sa1iram; //SA-1
SA1BWRAM sa1bwram; //SA-1
CC1BWRAM cc1bwram; //S-CPU
BitmapRAM bitmapram; //SA-1
}
//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data.
//this is used both to avoid VBR-reads from accessing MMIO registers, and
//to avoid syncing the S-CPU and SA-1*; as both chips are able to access
//these ports.
//(* eg, memory::cartram is used directly, as memory::sa1bwram syncs to the S-CPU)
void VBRBus::init() {
map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped);
map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::iram);
map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cartram);
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cartram);
map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::iram);
map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cartram);
map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
}
void SA1Bus::init() {
map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped);
for(unsigned i = 0x2200; i <= 0x23ff; i++) memory::mmio.map(i, sa1);
map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::sa1iram);
map(MapDirect, 0x00, 0x3f, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::sa1iram);
map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram);
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::sa1bwram);
map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bitmapram);
map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::sa1iram);
map(MapDirect, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::sa1iram);
map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram);
map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::cpuiram);
bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::cpuiram);
bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
memory::vectorsp.sync();
}
//===================
//VectorSelectionPage
//===================
//this class maps $00:[ff00-ffff] for the purpose of supporting:
//$2209.d6 IVSW (S-CPU IRQ vector selection) (0 = cart, 1 = SA-1)
//$2209.d4 NVSW (S-CPU NMI vector selection) (0 = cart, 1 = SA-1)
//when set, vector addresses are over-ridden with SA-1 register settings:
//SIV = S-CPU IRQ vector address override
//SNV = S-CPU NMI vector address override
//
//$00:[ffea-ffeb|ffee-ffef] are special cased on read;
//all other addresses return original mapped data.
uint8 VectorSelectionPage::read(unsigned addr) {
switch(0xff00 | (addr & 0xff)) {
case 0xffea: case 0xffeb: {
if(sa1.mmio.cpu_nvsw == true) return (sa1.mmio.snv >> ((addr & 1) << 3));
} break;
case 0xffee: case 0xffef: {
if(sa1.mmio.cpu_ivsw == true) return (sa1.mmio.siv >> ((addr & 1) << 3));
} break;
}
return access->read(addr);
}
void VectorSelectionPage::write(unsigned addr, uint8 data) {
return access->write(addr, data);
}
//call this whenever bus is remapped.
//note: S-CPU and SA-1 bus always share $00:[ff00-ffff] as cartridge ROM data;
//the SA-1 MMC does not allow mapping these independently between processors.
//this allows this class to be shared for both, caching only ones' access class.
void VectorSelectionPage::sync() {
if(bus.page[0x00ff00 >> 8].access != this) {
//bus was re-mapped, hook access routine
access = bus.page[0x00ff00 >> 8].access;
bus.page[0x00ff00 >> 8].access = this;
sa1bus.page[0x00ff00 >> 8].access = this;
}
}
//=======
//SA1IRAM
//=======
unsigned SA1IRAM::size() const {
return memory::iram.size();
}
uint8 SA1IRAM::read(unsigned addr) {
scheduler.sync_copcpu();
return memory::iram.read(addr);
}
void SA1IRAM::write(unsigned addr, uint8 data) {
scheduler.sync_copcpu();
memory::iram.write(addr, data);
}
//=======
//CPUIRAM
//=======
unsigned CPUIRAM::size() const {
return memory::iram.size();
}
uint8 CPUIRAM::read(unsigned addr) {
scheduler.sync_cpucop();
return memory::iram.read(addr);
}
void CPUIRAM::write(unsigned addr, uint8 data) {
scheduler.sync_cpucop();
memory::iram.write(addr, data);
}
//========
//SA1BWRAM
//========
unsigned SA1BWRAM::size() const {
return memory::cartram.size();
}
uint8 SA1BWRAM::read(unsigned addr) {
scheduler.sync_copcpu();
return memory::cartram.read(addr);
}
void SA1BWRAM::write(unsigned addr, uint8 data) {
scheduler.sync_copcpu();
memory::cartram.write(addr, data);
}
//========
//CC1BWRAM
//========
unsigned CC1BWRAM::size() const {
return memory::cartram.size();
}
uint8 CC1BWRAM::read(unsigned addr) {
scheduler.sync_cpucop();
if(dma) return sa1.dma_cc1_read(addr);
return memory::cartram.read(addr);
}
void CC1BWRAM::write(unsigned addr, uint8 data) {
scheduler.sync_cpucop();
memory::cartram.write(addr, data);
}
//=========
//BitmapRAM
//=========
unsigned BitmapRAM::size() const {
return 0x100000;
}
uint8 BitmapRAM::read(unsigned addr) {
scheduler.sync_copcpu();
if(sa1.mmio.bbf == 0) {
//4bpp
unsigned shift = addr & 1;
addr = (addr >> 1) & (memory::cartram.size() - 1);
switch(shift) { default:
case 0: return (memory::cartram.read(addr) >> 0) & 15;
case 1: return (memory::cartram.read(addr) >> 4) & 15;
}
} else {
//2bpp
unsigned shift = addr & 3;
addr = (addr >> 2) & (memory::cartram.size() - 1);
switch(shift) { default:
case 0: return (memory::cartram.read(addr) >> 0) & 3;
case 1: return (memory::cartram.read(addr) >> 2) & 3;
case 2: return (memory::cartram.read(addr) >> 4) & 3;
case 3: return (memory::cartram.read(addr) >> 6) & 3;
}
}
}
void BitmapRAM::write(unsigned addr, uint8 data) {
scheduler.sync_copcpu();
if(sa1.mmio.bbf == 0) {
//4bpp
unsigned shift = addr & 1;
addr = (addr >> 1) & (memory::cartram.size() - 1);
switch(shift) { default:
case 0: data = (memory::cartram.read(addr) & 0xf0) | ((data & 15) << 0); break;
case 1: data = (memory::cartram.read(addr) & 0x0f) | ((data & 15) << 4); break;
}
} else {
//2bpp
unsigned shift = addr & 3;
addr = (addr >> 2) & (memory::cartram.size() - 1);
switch(shift) { default:
case 0: data = (memory::cartram.read(addr) & 0xfc) | ((data & 3) << 0); break;
case 1: data = (memory::cartram.read(addr) & 0xf3) | ((data & 3) << 2); break;
case 2: data = (memory::cartram.read(addr) & 0xcf) | ((data & 3) << 4); break;
case 3: data = (memory::cartram.read(addr) & 0x3f) | ((data & 3) << 6); break;
}
}
memory::cartram.write(addr, data);
}
#endif

56
src/chip/sa1/bus/bus.hpp Normal file
View File

@@ -0,0 +1,56 @@
struct VBRBus : Bus {
void init();
};
struct SA1Bus : Bus {
void init();
};
struct VectorSelectionPage : Memory {
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
void sync();
Memory *access;
};
struct CPUIRAM : Memory {
unsigned size() const;
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
};
struct SA1IRAM : Memory {
unsigned size() const;
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
};
struct SA1BWRAM : Memory {
unsigned size() const;
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
};
struct CC1BWRAM : Memory {
unsigned size() const;
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
bool dma;
};
struct BitmapRAM : Memory {
unsigned size() const;
alwaysinline uint8 read(unsigned);
alwaysinline void write(unsigned, uint8);
};
namespace memory {
extern StaticRAM iram;
extern VectorSelectionPage vectorsp;
extern CPUIRAM cpuiram;
extern SA1IRAM sa1iram;
extern SA1BWRAM sa1bwram;
extern CC1BWRAM cc1bwram;
extern BitmapRAM bitmapram;
};

139
src/chip/sa1/dma/dma.cpp Normal file
View File

@@ -0,0 +1,139 @@
#ifdef SA1_CPP
//====================
//direct data transfer
//====================
void SA1::dma_normal() {
while(mmio.dtc--) {
uint8 data = regs.mdr;
uint32 dsa = mmio.dsa++;
uint32 dda = mmio.dda++;
//source and destination cannot be the same
if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestBWRAM) continue;
if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestIRAM ) continue;
switch(mmio.sd) {
case DMA::SourceROM: {
if((dsa & 0x408000) == 0x008000 || (dsa & 0xc00000) == 0xc00000) {
data = sa1bus.read(dsa);
}
} break;
case DMA::SourceBWRAM: {
if((dsa & 0x40e000) == 0x006000 || (dsa & 0xf00000) == 0x400000) {
data = sa1bus.read(dsa);
}
} break;
case DMA::SourceIRAM: {
data = memory::iram.read(dsa & 0x07ff);
} break;
}
switch(mmio.dd) {
case DMA::DestBWRAM: {
if((dda & 0x40e000) == 0x006000 || (dda & 0xf00000) == 0x400000) {
sa1bus.write(dda, data);
}
} break;
case DMA::DestIRAM: {
memory::iram.write(dda & 0x07ff, data);
} break;
}
}
mmio.dma_irqfl = true;
if(mmio.dma_irqen) mmio.dma_irqcl = 0;
}
//((byte & 6) << 3) + (byte & 1) explanation:
//transforms a byte index (0-7) into a planar index:
//result[] = { 0, 1, 16, 17, 32, 33, 48, 49 };
//works for 2bpp, 4bpp and 8bpp modes
//===========================
//type-1 character conversion
//===========================
void SA1::dma_cc1() {
memory::cc1bwram.dma = true;
mmio.chdma_irqfl = true;
if(mmio.chdma_irqen) {
mmio.chdma_irqcl = 0;
cpu.regs.irq = 1;
}
}
uint8 SA1::dma_cc1_read(unsigned addr) {
//16 bytes/char (2bpp); 32 bytes/char (4bpp); 64 bytes/char (8bpp)
unsigned charmask = (1 << (6 - mmio.dmacb)) - 1;
if((addr & charmask) == 0) {
//buffer next character to I-RAM
unsigned bpp = 2 << (2 - mmio.dmacb);
unsigned bpl = (8 << mmio.dmasize) >> mmio.dmacb;
unsigned bwmask = memory::cartram.size() - 1;
unsigned tile = ((addr - mmio.dsa) & bwmask) >> (6 - mmio.dmacb);
unsigned ty = (tile >> mmio.dmasize);
unsigned tx = tile & ((1 << mmio.dmasize) - 1);
unsigned bwaddr = mmio.dsa + ty * 8 * bpl + tx * bpp;
for(unsigned y = 0; y < 8; y++) {
uint64 data = 0;
for(unsigned byte = 0; byte < bpp; byte++) {
data |= (uint64)memory::cartram.read((bwaddr + byte) & bwmask) << (byte << 3);
}
bwaddr += bpl;
uint8 out[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
for(unsigned x = 0; x < 8; x++) {
out[0] |= (data & 1) << (7 - x); data >>= 1;
out[1] |= (data & 1) << (7 - x); data >>= 1;
if(mmio.dmacb == 2) continue;
out[2] |= (data & 1) << (7 - x); data >>= 1;
out[3] |= (data & 1) << (7 - x); data >>= 1;
if(mmio.dmacb == 1) continue;
out[4] |= (data & 1) << (7 - x); data >>= 1;
out[5] |= (data & 1) << (7 - x); data >>= 1;
out[6] |= (data & 1) << (7 - x); data >>= 1;
out[7] |= (data & 1) << (7 - x); data >>= 1;
}
for(unsigned byte = 0; byte < bpp; byte++) {
unsigned p = mmio.dda + (y << 1) + ((byte & 6) << 3) + (byte & 1);
memory::iram.write(p & 0x07ff, out[byte]);
}
}
}
return memory::iram.read((mmio.dda + (addr & charmask)) & 0x07ff);
}
//===========================
//type-2 character conversion
//===========================
void SA1::dma_cc2() {
//select register file index (0-7 or 8-15)
const uint8 *brf = &mmio.brf[(dma.line & 1) << 3];
unsigned bpp = 2 << (2 - mmio.dmacb);
unsigned addr = mmio.dda & 0x07ff;
addr &= ~((1 << (7 - mmio.dmacb)) - 1);
addr += (dma.line & 8) * bpp;
addr += (dma.line & 7) * 2;
for(unsigned byte = 0; byte < bpp; byte++) {
uint8 output = 0;
for(unsigned bit = 0; bit < 8; bit++) {
output |= ((brf[bit] >> byte) & 1) << (7 - bit);
}
memory::iram.write(addr + ((byte & 6) << 3) + (byte & 1), output);
}
dma.line = (dma.line + 1) & 15;
}
#endif

11
src/chip/sa1/dma/dma.hpp Normal file
View File

@@ -0,0 +1,11 @@
struct DMA {
enum CDEN { DmaNormal = 0, DmaCharConversion = 1 };
enum SD { SourceROM = 0, SourceBWRAM = 1, SourceIRAM = 2 };
enum DD { DestIRAM = 0, DestBWRAM = 1 };
unsigned line;
} dma;
void dma_normal();
void dma_cc1();
uint8 dma_cc1_read(unsigned addr);
void dma_cc2();

View File

@@ -0,0 +1,24 @@
#ifdef SA1_CPP
//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks)
//BW-RAM is accessed at ~5.37MHz (4 clock ticks)
//tick() == 2 clock ticks
//note: bus conflict delays are not emulated at this time
void SA1::op_io() {
tick();
}
uint8 SA1::op_read(unsigned addr) {
tick();
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
return sa1bus.read(addr);
}
void SA1::op_write(unsigned addr, uint8 data) {
tick();
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
sa1bus.write(addr, data);
}
#endif

View File

@@ -0,0 +1,5 @@
alwaysinline void op_io();
alwaysinline uint8 op_read(unsigned addr);
alwaysinline void op_write(unsigned addr, uint8 data);
uint8_t vbr_read(unsigned addr);

633
src/chip/sa1/mmio/mmio.cpp Normal file
View File

@@ -0,0 +1,633 @@
#ifdef SA1_CPP
//BS-X flash carts, when present, are mapped to 0x400000+
Memory& SA1::mmio_access(unsigned &addr) {
if(!memory::bsxflash.data()) return memory::cartrom;
if(addr < 0x400000) return memory::cartrom;
addr &= 0x3fffff;
return bsxflash;
}
//(CCNT) SA-1 control
void SA1::mmio_w2200(uint8 data) {
if(mmio.sa1_resb && !(data & 0x80)) {
//reset SA-1 CPU
regs.pc.w = mmio.crv;
regs.pc.b = 0x00;
}
mmio.sa1_irq = (data & 0x80);
mmio.sa1_rdyb = (data & 0x40);
mmio.sa1_resb = (data & 0x20);
mmio.sa1_nmi = (data & 0x10);
mmio.smeg = (data & 0x0f);
if(mmio.sa1_irq) {
mmio.sa1_irqfl = true;
if(mmio.sa1_irqen) mmio.sa1_irqcl = 0;
}
if(mmio.sa1_nmi) {
mmio.sa1_nmifl = true;
if(mmio.sa1_nmien) mmio.sa1_nmicl = 0;
}
}
//(SIE) S-CPU interrupt enable
void SA1::mmio_w2201(uint8 data) {
if(!mmio.cpu_irqen && (data & 0x80)) {
if(mmio.cpu_irqfl) {
mmio.cpu_irqcl = 0;
cpu.regs.irq = 1;
}
}
if(!mmio.chdma_irqen && (data & 0x20)) {
if(mmio.chdma_irqfl) {
mmio.chdma_irqcl = 0;
cpu.regs.irq = 1;
}
}
mmio.cpu_irqen = (data & 0x80);
mmio.chdma_irqen = (data & 0x20);
}
//(SIC) S-CPU interrupt clear
void SA1::mmio_w2202(uint8 data) {
mmio.cpu_irqcl = (data & 0x80);
mmio.chdma_irqcl = (data & 0x20);
if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false;
if(mmio.chdma_irqcl) mmio.chdma_irqfl = false;
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.regs.irq = 0;
}
//(CRV) SA-1 reset vector
void SA1::mmio_w2203(uint8 data) { mmio.crv = (mmio.crv & 0xff00) | data; }
void SA1::mmio_w2204(uint8 data) { mmio.crv = (data << 8) | (mmio.crv & 0xff); }
//(CNV) SA-1 NMI vector
void SA1::mmio_w2205(uint8 data) { mmio.cnv = (mmio.cnv & 0xff00) | data; }
void SA1::mmio_w2206(uint8 data) { mmio.cnv = (data << 8) | (mmio.cnv & 0xff); }
//(CIV) SA-1 IRQ vector
void SA1::mmio_w2207(uint8 data) { mmio.civ = (mmio.civ & 0xff00) | data; }
void SA1::mmio_w2208(uint8 data) { mmio.civ = (data << 8) | (mmio.civ & 0xff); }
//(SCNT) S-CPU control
void SA1::mmio_w2209(uint8 data) {
mmio.cpu_irq = (data & 0x80);
mmio.cpu_ivsw = (data & 0x40);
mmio.cpu_nvsw = (data & 0x10);
mmio.cmeg = (data & 0x0f);
if(mmio.cpu_irq) {
mmio.cpu_irqfl = true;
if(mmio.cpu_irqen) {
mmio.cpu_irqcl = 0;
cpu.regs.irq = 1;
}
}
}
//(CIE) SA-1 interrupt enable
void SA1::mmio_w220a(uint8 data) {
if(!mmio.sa1_irqen && (data & 0x80) && mmio.sa1_irqfl ) mmio.sa1_irqcl = 0;
if(!mmio.timer_irqen && (data & 0x40) && mmio.timer_irqfl) mmio.timer_irqcl = 0;
if(!mmio.dma_irqen && (data & 0x20) && mmio.dma_irqfl ) mmio.dma_irqcl = 0;
if(!mmio.sa1_nmien && (data & 0x10) && mmio.sa1_nmifl ) mmio.sa1_nmicl = 0;
mmio.sa1_irqen = (data & 0x80);
mmio.timer_irqen = (data & 0x40);
mmio.dma_irqen = (data & 0x20);
mmio.sa1_nmien = (data & 0x10);
}
//(CIC) SA-1 interrupt clear
void SA1::mmio_w220b(uint8 data) {
mmio.sa1_irqcl = (data & 0x80);
mmio.timer_irqcl = (data & 0x40);
mmio.dma_irqcl = (data & 0x20);
mmio.sa1_nmicl = (data & 0x10);
if(mmio.sa1_irqcl) mmio.sa1_irqfl = false;
if(mmio.timer_irqcl) mmio.timer_irqfl = false;
if(mmio.dma_irqcl) mmio.dma_irqfl = false;
if(mmio.sa1_nmicl) mmio.sa1_nmifl = false;
}
//(SNV) S-CPU NMI vector
void SA1::mmio_w220c(uint8 data) { mmio.snv = (mmio.snv & 0xff00) | data; }
void SA1::mmio_w220d(uint8 data) { mmio.snv = (data << 8) | (mmio.snv & 0xff); }
//(SIV) S-CPU IRQ vector
void SA1::mmio_w220e(uint8 data) { mmio.siv = (mmio.siv & 0xff00) | data; }
void SA1::mmio_w220f(uint8 data) { mmio.siv = (data << 8) | (mmio.siv & 0xff); }
//(TMC) H/V timer control
void SA1::mmio_w2210(uint8 data) {
mmio.hvselb = (data & 0x80);
mmio.ven = (data & 0x02);
mmio.hen = (data & 0x01);
}
//(CTR) SA-1 timer restart
void SA1::mmio_w2211(uint8 data) {
status.vcounter = 0;
status.hcounter = 0;
}
//(HCNT) H-count
void SA1::mmio_w2212(uint8 data) { mmio.hcnt = (mmio.hcnt & 0xff00) | (data << 0); }
void SA1::mmio_w2213(uint8 data) { mmio.hcnt = (mmio.hcnt & 0x00ff) | (data << 8); }
//(VCNT) V-count
void SA1::mmio_w2214(uint8 data) { mmio.vcnt = (mmio.vcnt & 0xff00) | (data << 0); }
void SA1::mmio_w2215(uint8 data) { mmio.vcnt = (mmio.vcnt & 0x00ff) | (data << 8); }
//(CXB) Super MMC bank C
void SA1::mmio_w2220(uint8 data) {
mmio.cbmode = (data & 0x80);
mmio.cb = (data & 0x07);
unsigned addr = mmio.cb << 20;
Memory &access = mmio_access(addr);
if(mmio.cbmode == 0) {
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000);
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000);
} else {
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr);
memory::vectorsp.sync();
}
//(DXB) Super MMC bank D
void SA1::mmio_w2221(uint8 data) {
mmio.dbmode = (data & 0x80);
mmio.db = (data & 0x07);
unsigned addr = mmio.db << 20;
Memory &access = mmio_access(addr);
if(mmio.dbmode == 0) {
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000);
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000);
} else {
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr);
}
//(EXB) Super MMC bank E
void SA1::mmio_w2222(uint8 data) {
mmio.ebmode = (data & 0x80);
mmio.eb = (data & 0x07);
unsigned addr = mmio.eb << 20;
Memory &access = mmio_access(addr);
if(mmio.ebmode == 0) {
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000);
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000);
} else {
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr);
}
//(FXB) Super MMC bank F
void SA1::mmio_w2223(uint8 data) {
mmio.fbmode = (data & 0x80);
mmio.fb = (data & 0x07);
unsigned addr = mmio.fb << 20;
Memory &access = mmio_access(addr);
if(mmio.fbmode == 0) {
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000);
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000);
} else {
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr);
}
//(BMAPS) S-CPU BW-RAM address mapping
void SA1::mmio_w2224(uint8 data) {
mmio.sbm = (data & 0x1f);
bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000);
bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000);
}
//(BMAP) SA-1 BW-RAM address mapping
void SA1::mmio_w2225(uint8 data) {
mmio.sw46 = (data & 0x80);
mmio.cbm = (data & 0x7f);
if(mmio.sw46 == 0) {
//$[40-43]:[0000-ffff] x 32 projection
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
} else {
//$[60-6f]:[0000-ffff] x 128 projection
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000);
}
}
//(SWBE) S-CPU BW-RAM write enable
void SA1::mmio_w2226(uint8 data) {
mmio.swen = (data & 0x80);
}
//(CWBE) SA-1 BW-RAM write enable
void SA1::mmio_w2227(uint8 data) {
mmio.cwen = (data & 0x80);
}
//(BWPA) BW-RAM write-protected area
void SA1::mmio_w2228(uint8 data) {
mmio.bwp = (data & 0x0f);
}
//(SIWP) S-CPU I-RAM write protection
void SA1::mmio_w2229(uint8 data) {
mmio.siwp = data;
}
//(CIWP) SA-1 I-RAM write protection
void SA1::mmio_w222a(uint8 data) {
mmio.ciwp = data;
}
//(DCNT) DMA control
void SA1::mmio_w2230(uint8 data) {
mmio.dmaen = (data & 0x80);
mmio.dprio = (data & 0x40);
mmio.cden = (data & 0x20);
mmio.cdsel = (data & 0x10);
mmio.dd = (data & 0x04);
mmio.sd = (data & 0x03);
if(mmio.dmaen == 0) dma.line = 0;
}
//(CDMA) character conversion DMA parameters
void SA1::mmio_w2231(uint8 data) {
mmio.chdend = (data & 0x80);
mmio.dmasize = (data >> 2) & 7;
mmio.dmacb = (data & 0x03);
if(mmio.chdend) memory::cc1bwram.dma = false;
if(mmio.dmasize > 5) mmio.dmasize = 5;
if(mmio.dmacb > 2) mmio.dmacb = 2;
}
//(SDA) DMA source device start address
void SA1::mmio_w2232(uint8 data) { mmio.dsa = (mmio.dsa & 0xffff00) | (data << 0); }
void SA1::mmio_w2233(uint8 data) { mmio.dsa = (mmio.dsa & 0xff00ff) | (data << 8); }
void SA1::mmio_w2234(uint8 data) { mmio.dsa = (mmio.dsa & 0x00ffff) | (data << 16); }
//(DDA) DMA destination start address
void SA1::mmio_w2235(uint8 data) {
mmio.dda = (mmio.dda & 0xffff00) | (data << 0);
}
void SA1::mmio_w2236(uint8 data) {
mmio.dda = (mmio.dda & 0xff00ff) | (data << 8);
if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) {
dma_normal();
} else if(mmio.cden == 1 && mmio.cdsel == 1) {
dma_cc1();
}
}
}
void SA1::mmio_w2237(uint8 data) {
mmio.dda = (mmio.dda & 0x00ffff) | (data << 16);
if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestBWRAM) {
dma_normal();
}
}
}
//(DTC) DMA terminal counter
void SA1::mmio_w2238(uint8 data) { mmio.dtc = (mmio.dtc & 0xff00) | (data << 0); }
void SA1::mmio_w2239(uint8 data) { mmio.dtc = (mmio.dtc & 0x00ff) | (data << 8); }
//(BBF) BW-RAM bitmap format
void SA1::mmio_w223f(uint8 data) {
mmio.bbf = (data & 0x80);
}
//(BRF) bitmap register files
void SA1::mmio_w2240(uint8 data) { mmio.brf[ 0] = data; }
void SA1::mmio_w2241(uint8 data) { mmio.brf[ 1] = data; }
void SA1::mmio_w2242(uint8 data) { mmio.brf[ 2] = data; }
void SA1::mmio_w2243(uint8 data) { mmio.brf[ 3] = data; }
void SA1::mmio_w2244(uint8 data) { mmio.brf[ 4] = data; }
void SA1::mmio_w2245(uint8 data) { mmio.brf[ 5] = data; }
void SA1::mmio_w2246(uint8 data) { mmio.brf[ 6] = data; }
void SA1::mmio_w2247(uint8 data) { mmio.brf[ 7] = data;
if(mmio.dmaen == true) {
if(mmio.cden == 1 && mmio.cdsel == 0) {
dma_cc2();
}
}
}
void SA1::mmio_w2248(uint8 data) { mmio.brf[ 8] = data; }
void SA1::mmio_w2249(uint8 data) { mmio.brf[ 9] = data; }
void SA1::mmio_w224a(uint8 data) { mmio.brf[10] = data; }
void SA1::mmio_w224b(uint8 data) { mmio.brf[11] = data; }
void SA1::mmio_w224c(uint8 data) { mmio.brf[12] = data; }
void SA1::mmio_w224d(uint8 data) { mmio.brf[13] = data; }
void SA1::mmio_w224e(uint8 data) { mmio.brf[14] = data; }
void SA1::mmio_w224f(uint8 data) { mmio.brf[15] = data;
if(mmio.dmaen == true) {
if(mmio.cden == 1 && mmio.cdsel == 0) {
dma_cc2();
}
}
}
//(MCNT) arithmetic control
void SA1::mmio_w2250(uint8 data) {
mmio.acm = (data & 0x02);
mmio.md = (data & 0x01);
if(mmio.acm) mmio.mr = 0;
}
//(MAL) multiplicand / dividend low
void SA1::mmio_w2251(uint8 data) {
mmio.ma = (mmio.ma & 0xff00) | data;
}
//(MAH) multiplicand / dividend high
void SA1::mmio_w2252(uint8 data) {
mmio.ma = (data << 8) | (mmio.ma & 0x00ff);
}
//(MBL) multiplier / divisor low
void SA1::mmio_w2253(uint8 data) {
mmio.mb = (mmio.mb & 0xff00) | data;
}
//(MBH) multiplier / divisor high
//multiplication / cumulative sum only resets MB
//division resets both MA and MB
void SA1::mmio_w2254(uint8 data) {
mmio.mb = (data << 8) | (mmio.mb & 0x00ff);
if(mmio.acm == 0) {
if(mmio.md == 0) {
//signed multiplication
mmio.mr = (int16)mmio.ma * (int16)mmio.mb;
mmio.mb = 0;
} else {
//unsigned division
if(mmio.mb == 0) {
mmio.mr = 0;
} else {
int16 quotient = (int16)mmio.ma / (uint16)mmio.mb;
uint16 remainder = (int16)mmio.ma % (uint16)mmio.mb;
mmio.mr = (remainder << 16) | quotient;
}
mmio.ma = 0;
mmio.mb = 0;
}
} else {
//sigma (accumulative multiplication)
mmio.mr += (int16)mmio.ma * (int16)mmio.mb;
mmio.overflow = (mmio.mr >= (1ULL << 40));
mmio.mr &= (1ULL << 40) - 1;
mmio.mb = 0;
}
}
//(VBD) variable-length bit processing
void SA1::mmio_w2258(uint8 data) {
mmio.hl = (data & 0x80);
mmio.vb = (data & 0x0f);
if(mmio.vb == 0) mmio.vb = 16;
if(mmio.hl == 0) {
//fixed mode
mmio.vbit += mmio.vb;
mmio.va += (mmio.vbit >> 3);
mmio.vbit &= 7;
}
}
//(VDA) variable-length bit game pak ROM start address
void SA1::mmio_w2259(uint8 data) { mmio.va = (mmio.va & 0xffff00) | (data << 0); }
void SA1::mmio_w225a(uint8 data) { mmio.va = (mmio.va & 0xff00ff) | (data << 8); }
void SA1::mmio_w225b(uint8 data) { mmio.va = (mmio.va & 0x00ffff) | (data << 16); mmio.vbit = 0; }
//(SFR) S-CPU flag read
uint8 SA1::mmio_r2300() {
uint8 data;
data = mmio.cpu_irqfl << 7;
data |= mmio.cpu_ivsw << 6;
data |= mmio.chdma_irqfl << 5;
data |= mmio.cpu_nvsw << 4;
data |= mmio.cmeg;
return data;
}
//(CFR) SA-1 flag read
uint8 SA1::mmio_r2301() {
uint8 data;
data = mmio.sa1_irqfl << 7;
data |= mmio.timer_irqfl << 6;
data |= mmio.dma_irqfl << 5;
data |= mmio.sa1_nmifl << 4;
data |= mmio.smeg;
return data;
}
//(HCR) hcounter read
uint8 SA1::mmio_r2302() {
//latch counters
mmio.hcr = status.hcounter >> 2;
mmio.vcr = status.vcounter;
return mmio.hcr >> 0; }
uint8 SA1::mmio_r2303() { return mmio.hcr >> 8; }
//(VCR) vcounter read
uint8 SA1::mmio_r2304() { return mmio.vcr >> 0; }
uint8 SA1::mmio_r2305() { return mmio.vcr >> 8; }
//(MR) arithmetic result
uint8 SA1::mmio_r2306() { return mmio.mr >> 0; }
uint8 SA1::mmio_r2307() { return mmio.mr >> 8; }
uint8 SA1::mmio_r2308() { return mmio.mr >> 16; }
uint8 SA1::mmio_r2309() { return mmio.mr >> 24; }
uint8 SA1::mmio_r230a() { return mmio.mr >> 32; }
//(OF) arithmetic overflow flag
uint8 SA1::mmio_r230b() { return mmio.overflow << 7; }
//(VDPL) variable-length data read port low
uint8 SA1::mmio_r230c() {
uint32 data = (vbrbus.read(mmio.va + 0) << 0)
| (vbrbus.read(mmio.va + 1) << 8)
| (vbrbus.read(mmio.va + 2) << 16);
data >>= mmio.vbit;
return data >> 0;
}
//(VDPH) variable-length data read port high
uint8 SA1::mmio_r230d() {
uint32 data = (vbrbus.read(mmio.va + 0) << 0)
| (vbrbus.read(mmio.va + 1) << 8)
| (vbrbus.read(mmio.va + 2) << 16);
data >>= mmio.vbit;
if(mmio.hl == 1) {
//auto-increment mode
mmio.vbit += mmio.vb;
mmio.va += (mmio.vbit >> 3);
mmio.vbit &= 7;
}
return data >> 8;
}
//(VC) version code register
uint8 SA1::mmio_r230e() {
return 0x01; //true value unknown
}
uint8 SA1::mmio_read(unsigned addr) {
(co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu());
addr &= 0xffff;
switch(addr) {
case 0x2300: return mmio_r2300();
case 0x2301: return mmio_r2301();
case 0x2302: return mmio_r2302();
case 0x2303: return mmio_r2303();
case 0x2304: return mmio_r2304();
case 0x2305: return mmio_r2305();
case 0x2306: return mmio_r2306();
case 0x2307: return mmio_r2307();
case 0x2308: return mmio_r2308();
case 0x2309: return mmio_r2309();
case 0x230a: return mmio_r230a();
case 0x230b: return mmio_r230b();
case 0x230c: return mmio_r230c();
case 0x230d: return mmio_r230d();
case 0x230e: return mmio_r230e();
}
return 0x00;
}
void SA1::mmio_write(unsigned addr, uint8 data) {
(co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu());
addr &= 0xffff;
switch(addr) {
case 0x2200: return mmio_w2200(data);
case 0x2201: return mmio_w2201(data);
case 0x2202: return mmio_w2202(data);
case 0x2203: return mmio_w2203(data);
case 0x2204: return mmio_w2204(data);
case 0x2205: return mmio_w2205(data);
case 0x2206: return mmio_w2206(data);
case 0x2207: return mmio_w2207(data);
case 0x2208: return mmio_w2208(data);
case 0x2209: return mmio_w2209(data);
case 0x220a: return mmio_w220a(data);
case 0x220b: return mmio_w220b(data);
case 0x220c: return mmio_w220c(data);
case 0x220d: return mmio_w220d(data);
case 0x220e: return mmio_w220e(data);
case 0x220f: return mmio_w220f(data);
case 0x2210: return mmio_w2210(data);
case 0x2211: return mmio_w2211(data);
case 0x2212: return mmio_w2212(data);
case 0x2213: return mmio_w2213(data);
case 0x2214: return mmio_w2214(data);
case 0x2215: return mmio_w2215(data);
case 0x2220: return mmio_w2220(data);
case 0x2221: return mmio_w2221(data);
case 0x2222: return mmio_w2222(data);
case 0x2223: return mmio_w2223(data);
case 0x2224: return mmio_w2224(data);
case 0x2225: return mmio_w2225(data);
case 0x2226: return mmio_w2226(data);
case 0x2227: return mmio_w2227(data);
case 0x2228: return mmio_w2228(data);
case 0x2229: return mmio_w2229(data);
case 0x222a: return mmio_w222a(data);
case 0x2230: return mmio_w2230(data);
case 0x2231: return mmio_w2231(data);
case 0x2232: return mmio_w2232(data);
case 0x2233: return mmio_w2233(data);
case 0x2234: return mmio_w2234(data);
case 0x2235: return mmio_w2235(data);
case 0x2236: return mmio_w2236(data);
case 0x2237: return mmio_w2237(data);
case 0x2238: return mmio_w2238(data);
case 0x2239: return mmio_w2239(data);
case 0x223f: return mmio_w223f(data);
case 0x2240: return mmio_w2240(data);
case 0x2241: return mmio_w2241(data);
case 0x2242: return mmio_w2242(data);
case 0x2243: return mmio_w2243(data);
case 0x2244: return mmio_w2244(data);
case 0x2245: return mmio_w2245(data);
case 0x2246: return mmio_w2246(data);
case 0x2247: return mmio_w2247(data);
case 0x2248: return mmio_w2248(data);
case 0x2249: return mmio_w2249(data);
case 0x224a: return mmio_w224a(data);
case 0x224b: return mmio_w224b(data);
case 0x224c: return mmio_w224c(data);
case 0x224d: return mmio_w224d(data);
case 0x224e: return mmio_w224e(data);
case 0x224f: return mmio_w224f(data);
case 0x2250: return mmio_w2250(data);
case 0x2251: return mmio_w2251(data);
case 0x2252: return mmio_w2252(data);
case 0x2253: return mmio_w2253(data);
case 0x2254: return mmio_w2254(data);
case 0x2258: return mmio_w2258(data);
case 0x2259: return mmio_w2259(data);
case 0x225a: return mmio_w225a(data);
case 0x225b: return mmio_w225b(data);
}
}
#endif

256
src/chip/sa1/mmio/mmio.hpp Normal file
View File

@@ -0,0 +1,256 @@
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
Memory& mmio_access(unsigned &addr);
struct MMIO {
//$2200 CCNT
bool sa1_irq;
bool sa1_rdyb;
bool sa1_resb;
bool sa1_nmi;
uint8 smeg;
//$2201 SIE
bool cpu_irqen;
bool chdma_irqen;
//$2202 SIC
bool cpu_irqcl;
bool chdma_irqcl;
//$2203,$2204 CRV
uint16 crv;
//$2205,$2206 CNV
uint16 cnv;
//$2207,$2208 CIV
uint16 civ;
//$2209 SCNT
bool cpu_irq;
bool cpu_ivsw;
bool cpu_nvsw;
uint8 cmeg;
//$220a CIE
bool sa1_irqen;
bool timer_irqen;
bool dma_irqen;
bool sa1_nmien;
//$220b CIC
bool sa1_irqcl;
bool timer_irqcl;
bool dma_irqcl;
bool sa1_nmicl;
//$220c,$220d SNV
uint16 snv;
//$220e,$220f SIV
uint16 siv;
//$2210 TMC
bool hvselb;
bool ven;
bool hen;
//$2212,$2213
uint16 hcnt;
//$2214,$2215
uint16 vcnt;
//$2220 CXB
bool cbmode;
uint8 cb;
//$2221 DXB
bool dbmode;
uint8 db;
//$2222 EXB
bool ebmode;
uint8 eb;
//$2223 FXB
bool fbmode;
uint8 fb;
//$2224 BMAPS
uint8 sbm;
//$2225 BMAP
bool sw46;
uint8 cbm;
//$2226 SBWE
bool swen;
//$2227 CBWE
bool cwen;
//$2228 BWPA
uint8 bwp;
//$2229 SIWP
uint8 siwp;
//$222a CIWP
uint8 ciwp;
//$2230 DCNT
bool dmaen;
bool dprio;
bool cden;
bool cdsel;
bool dd;
uint8 sd;
//$2231 CDMA
bool chdend;
uint8 dmasize;
uint8 dmacb;
//$2232-$2234 SDA
uint32 dsa;
//$2235-$2237 DDA
uint32 dda;
//$2238,$2239 DTC
uint16 dtc;
//$223f BBF
bool bbf;
//$2240-224f BRF
uint8 brf[16];
//$2250 MCNT
bool acm;
bool md;
//$2251,$2252 MA
uint16 ma;
//$2253,$2254 MB
uint16 mb;
//$2258 VBD
bool hl;
uint8 vb;
//$2259-$225b VDA
uint32 va;
uint8 vbit;
//$2300 SFR
bool cpu_irqfl;
bool chdma_irqfl;
//$2301 CFR
bool sa1_irqfl;
bool timer_irqfl;
bool dma_irqfl;
bool sa1_nmifl;
//$2302,$2303 HCR
uint16 hcr;
//$2304,$2305 VCR
uint16 vcr;
//$2306-230a MR
uint64 mr;
//$230b OF
bool overflow;
} mmio;
void mmio_w2200(uint8); //CCNT
void mmio_w2201(uint8); //SIE
void mmio_w2202(uint8); //SIC
void mmio_w2203(uint8); //CRVL
void mmio_w2204(uint8); //CRVH
void mmio_w2205(uint8); //CNVL
void mmio_w2206(uint8); //CNVH
void mmio_w2207(uint8); //CIVL
void mmio_w2208(uint8); //CIVH
void mmio_w2209(uint8); //SCNT
void mmio_w220a(uint8); //CIE
void mmio_w220b(uint8); //CIC
void mmio_w220c(uint8); //SNVL
void mmio_w220d(uint8); //SNVH
void mmio_w220e(uint8); //SIVL
void mmio_w220f(uint8); //SIVH
void mmio_w2210(uint8); //TMC
void mmio_w2211(uint8); //CTR
void mmio_w2212(uint8); //HCNTL
void mmio_w2213(uint8); //HCNTH
void mmio_w2214(uint8); //VCNTL
void mmio_w2215(uint8); //VCNTH
void mmio_w2220(uint8); //CXB
void mmio_w2221(uint8); //DXB
void mmio_w2222(uint8); //EXB
void mmio_w2223(uint8); //FXB
void mmio_w2224(uint8); //BMAPS
void mmio_w2225(uint8); //BMAP
void mmio_w2226(uint8); //SBWE
void mmio_w2227(uint8); //CBWE
void mmio_w2228(uint8); //BWPA
void mmio_w2229(uint8); //SIWP
void mmio_w222a(uint8); //CIWP
void mmio_w2230(uint8); //DCNT
void mmio_w2231(uint8); //CDMA
void mmio_w2232(uint8); //SDAL
void mmio_w2233(uint8); //SDAH
void mmio_w2234(uint8); //SDAB
void mmio_w2235(uint8); //DDAL
void mmio_w2236(uint8); //DDAH
void mmio_w2237(uint8); //DDAB
void mmio_w2238(uint8); //DTCL
void mmio_w2239(uint8); //DTCH
void mmio_w223f(uint8); //BBF
void mmio_w2240(uint8); //BRF0
void mmio_w2241(uint8); //BRF1
void mmio_w2242(uint8); //BRF2
void mmio_w2243(uint8); //BRF3
void mmio_w2244(uint8); //BRF4
void mmio_w2245(uint8); //BRF5
void mmio_w2246(uint8); //BRF6
void mmio_w2247(uint8); //BRF7
void mmio_w2248(uint8); //BRF8
void mmio_w2249(uint8); //BRF9
void mmio_w224a(uint8); //BRFA
void mmio_w224b(uint8); //BRFB
void mmio_w224c(uint8); //BRFC
void mmio_w224d(uint8); //BRFD
void mmio_w224e(uint8); //BRFE
void mmio_w224f(uint8); //BRFF
void mmio_w2250(uint8); //MCNT
void mmio_w2251(uint8); //MAL
void mmio_w2252(uint8); //MAH
void mmio_w2253(uint8); //MBL
void mmio_w2254(uint8); //MBH
void mmio_w2258(uint8); //VBD
void mmio_w2259(uint8); //VDAL
void mmio_w225a(uint8); //VDAH
void mmio_w225b(uint8); //VDAB
uint8 mmio_r2300(); //SFR
uint8 mmio_r2301(); //CFR
uint8 mmio_r2302(); //HCRL
uint8 mmio_r2303(); //HCRH
uint8 mmio_r2304(); //VCRL
uint8 mmio_r2305(); //VCRH
uint8 mmio_r2306(); //MR [00-07]
uint8 mmio_r2307(); //MR [08-15]
uint8 mmio_r2308(); //MR [16-23]
uint8 mmio_r2309(); //MR [24-31]
uint8 mmio_r230a(); //MR [32-40]
uint8 mmio_r230b(); //OF
uint8 mmio_r230c(); //VDPL
uint8 mmio_r230d(); //VDPH
uint8 mmio_r230e(); //VC

327
src/chip/sa1/sa1.cpp Normal file
View File

@@ -0,0 +1,327 @@
#include <../base.hpp>
#define SA1_CPP
namespace SNES {
SA1 sa1;
#include "serialization.cpp"
#include "bus/bus.cpp"
#include "dma/dma.cpp"
#include "memory/memory.cpp"
#include "mmio/mmio.cpp"
void SA1::enter() {
while(true) {
if(scheduler.sync == Scheduler::SyncAll) {
scheduler.exit(Scheduler::SynchronizeEvent);
}
if(mmio.sa1_rdyb || mmio.sa1_resb) {
//SA-1 co-processor is asleep
tick();
scheduler.sync_copcpu();
continue;
}
if(status.interrupt_pending) {
status.interrupt_pending = false;
interrupt(status.interrupt_vector);
}
(this->*opcode_table[op_readpc()])();
}
}
void SA1::last_cycle() {
if(mmio.sa1_nmi && !mmio.sa1_nmicl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.cnv;
mmio.sa1_nmifl = true;
mmio.sa1_nmicl = 1;
regs.wai = false;
} else if(!regs.p.i) {
if(mmio.timer_irqen && !mmio.timer_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.timer_irqfl = true;
regs.wai = false;
} else if(mmio.dma_irqen && !mmio.dma_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.dma_irqfl = true;
regs.wai = false;
} else if(mmio.sa1_irq && !mmio.sa1_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.sa1_irqfl = true;
regs.wai = false;
}
}
}
void SA1::interrupt(uint16 vector) {
op_read(regs.pc.d);
op_io();
if(!regs.e) op_writestack(regs.pc.b);
op_writestack(regs.pc.h);
op_writestack(regs.pc.l);
op_writestack(regs.e ? (regs.p & ~0x10) : regs.p);
regs.pc.w = vector;
regs.pc.b = 0x00;
regs.p.i = 1;
regs.p.d = 0;
}
bool SA1::interrupt_pending() {
return status.interrupt_pending;
}
void SA1::tick() {
scheduler.addclocks_cop(2);
if(++status.tick_counter == 0) scheduler.sync_copcpu();
//adjust counters:
//note that internally, status counters are in clocks;
//whereas MMIO register counters are in dots (4 clocks = 1 dot)
if(mmio.hvselb == 0) {
//HV timer
status.hcounter += 2;
if(status.hcounter >= 1364) {
status.hcounter = 0;
if(++status.vcounter >= status.scanlines) status.vcounter = 0;
}
} else {
//linear timer
status.hcounter += 2;
status.vcounter += (status.hcounter >> 11);
status.hcounter &= 0x07ff;
status.vcounter &= 0x01ff;
}
//test counters for timer IRQ
switch((mmio.ven << 1) + (mmio.hen << 0)) {
case 0: break;
case 1: if(status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break;
case 2: if(status.vcounter == mmio.vcnt && status.hcounter == 0) trigger_irq(); break;
case 3: if(status.vcounter == mmio.hcnt && status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break;
}
}
void SA1::trigger_irq() {
mmio.timer_irqfl = true;
if(mmio.timer_irqen) mmio.timer_irqcl = 0;
}
void SA1::init() {
}
void SA1::enable() {
}
void SA1::power() {
regs.a = regs.x = regs.y = 0x0000;
regs.s = 0x01ff;
reset();
}
void SA1::reset() {
memory::vectorsp.access = 0;
memory::cc1bwram.dma = false;
for(unsigned addr = 0; addr < memory::iram.size(); addr++) {
memory::iram.write(addr, 0x00);
}
vbrbus.init();
sa1bus.init();
regs.pc.d = 0x000000;
regs.x.h = 0x00;
regs.y.h = 0x00;
regs.s.h = 0x01;
regs.d = 0x0000;
regs.db = 0x00;
regs.p = 0x34;
regs.e = 1;
regs.mdr = 0x00;
regs.wai = false;
CPUcore::update_table();
status.tick_counter = 0;
status.interrupt_pending = false;
status.interrupt_vector = 0x0000;
status.scanlines = (system.region() == System::NTSC ? 262 : 312);
status.vcounter = 0;
status.hcounter = 0;
dma.line = 0;
//$2200 CCNT
mmio.sa1_irq = false;
mmio.sa1_rdyb = false;
mmio.sa1_resb = true;
mmio.sa1_nmi = false;
mmio.smeg = 0;
//$2201 SIE
mmio.cpu_irqen = false;
mmio.chdma_irqen = false;
//$2202 SIC
mmio.cpu_irqcl = false;
mmio.chdma_irqcl = false;
//$2203,$2204 CRV
mmio.crv = 0x0000;
//$2205,$2206 CNV
mmio.cnv = 0x0000;
//$2207,$2208 CIV
mmio.civ = 0x0000;
//$2209 SCNT
mmio.cpu_irq = false;
mmio.cpu_ivsw = false;
mmio.cpu_nvsw = false;
mmio.cmeg = 0;
//$220a CIE
mmio.sa1_irqen = false;
mmio.timer_irqen = false;
mmio.dma_irqen = false;
mmio.sa1_nmien = false;
//$220b CIC
mmio.sa1_irqcl = false;
mmio.timer_irqcl = false;
mmio.dma_irqcl = false;
mmio.sa1_nmicl = false;
//$220c,$220d SNV
mmio.snv = 0x0000;
//$220e,$220f SIV
mmio.siv = 0x0000;
//$2210
mmio.hvselb = false;
mmio.ven = false;
mmio.hen = false;
//$2212,$2213 HCNT
mmio.hcnt = 0x0000;
//$2214,$2215 VCNT
mmio.vcnt = 0x0000;
//$2220-2223 CXB, DXB, EXB, FXB
mmio.cbmode = 0;
mmio.dbmode = 0;
mmio.ebmode = 0;
mmio.fbmode = 0;
mmio.cb = 0x00;
mmio.db = 0x01;
mmio.eb = 0x02;
mmio.fb = 0x03;
//$2224 BMAPS
mmio.sbm = 0x00;
//$2225 BMAP
mmio.sw46 = false;
mmio.cbm = 0x00;
//$2226 SWBE
mmio.swen = false;
//$2227 CWBE
mmio.cwen = false;
//$2228 BWPA
mmio.bwp = 0x0f;
//$2229 SIWP
mmio.siwp = 0x00;
//$222a CIWP
mmio.ciwp = 0x00;
//$2230 DCNT
mmio.dmaen = false;
mmio.dprio = false;
mmio.cden = false;
mmio.cdsel = false;
mmio.dd = 0;
mmio.sd = 0;
//$2231 CDMA
mmio.chdend = false;
mmio.dmasize = 0;
mmio.dmacb = 0;
//$2232-$2234 SDA
mmio.dsa = 0x000000;
//$2235-$2237 DDA
mmio.dda = 0x000000;
//$2238,$2239 DTC
mmio.dtc = 0x0000;
//$223f BBF
mmio.bbf = 0;
//$2240-$224f BRF
for(unsigned i = 0; i < 16; i++) {
mmio.brf[i] = 0x00;
}
//$2250 MCNT
mmio.acm = 0;
mmio.md = 0;
//$2251,$2252 MA
mmio.ma = 0x0000;
//$2253,$2254 MB
mmio.mb = 0x0000;
//$2258 VBD
mmio.hl = false;
mmio.vb = 16;
//$2259-$225b
mmio.va = 0x000000;
mmio.vbit = 0;
//$2300 SFR
mmio.cpu_irqfl = false;
mmio.chdma_irqfl = false;
//$2301 CFR
mmio.sa1_irqfl = false;
mmio.timer_irqfl = false;
mmio.dma_irqfl = false;
mmio.sa1_nmifl = false;
//$2302,$2303 HCR
mmio.hcr = 0x0000;
//$2304,$2305 VCR
mmio.vcr = 0x0000;
//$2306-$230a MR
mmio.mr = 0;
//$230b
mmio.overflow = false;
}
SA1::SA1() {
}
};

38
src/chip/sa1/sa1.hpp Normal file
View File

@@ -0,0 +1,38 @@
#include "bus/bus.hpp"
class SA1 : public CPUcore, public MMIO {
public:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
struct Status {
uint8 tick_counter;
bool interrupt_pending;
uint16 interrupt_vector;
uint16 scanlines;
uint16 vcounter;
uint16 hcounter;
} status;
void enter();
void interrupt(uint16 vector);
void tick();
alwaysinline void trigger_irq();
alwaysinline void last_cycle();
alwaysinline bool interrupt_pending();
void init();
void enable();
void power();
void reset();
void serialize(serializer&);
SA1();
};
extern SA1 sa1;
extern SA1Bus sa1bus;

View File

@@ -0,0 +1,150 @@
#ifdef SA1_CPP
void SA1::serialize(serializer &s) {
CPUcore::core_serialize(s);
//sa1.hpp
s.integer(status.tick_counter);
s.integer(status.interrupt_pending);
s.integer(status.interrupt_vector);
s.integer(status.scanlines);
s.integer(status.vcounter);
s.integer(status.hcounter);
//bus/bus.hpp
s.array(memory::iram.data(), memory::iram.size());
memory::vectorsp.sync();
s.integer(memory::cc1bwram.dma);
//dma/dma.hpp
s.integer(dma.line);
//mmio/mmio.hpp
s.integer(mmio.sa1_irq);
s.integer(mmio.sa1_rdyb);
s.integer(mmio.sa1_resb);
s.integer(mmio.sa1_nmi);
s.integer(mmio.smeg);
s.integer(mmio.cpu_irqen);
s.integer(mmio.chdma_irqen);
s.integer(mmio.cpu_irqcl);
s.integer(mmio.chdma_irqcl);
s.integer(mmio.crv);
s.integer(mmio.cnv);
s.integer(mmio.civ);
s.integer(mmio.cpu_irq);
s.integer(mmio.cpu_ivsw);
s.integer(mmio.cpu_nvsw);
s.integer(mmio.cmeg);
s.integer(mmio.sa1_irqen);
s.integer(mmio.timer_irqen);
s.integer(mmio.dma_irqen);
s.integer(mmio.sa1_nmien);
s.integer(mmio.sa1_irqcl);
s.integer(mmio.timer_irqcl);
s.integer(mmio.dma_irqcl);
s.integer(mmio.sa1_nmicl);
s.integer(mmio.snv);
s.integer(mmio.siv);
s.integer(mmio.hvselb);
s.integer(mmio.ven);
s.integer(mmio.hen);
s.integer(mmio.hcnt);
s.integer(mmio.vcnt);
s.integer(mmio.cbmode);
s.integer(mmio.cb);
s.integer(mmio.dbmode);
s.integer(mmio.db);
s.integer(mmio.ebmode);
s.integer(mmio.eb);
s.integer(mmio.fbmode);
s.integer(mmio.fb);
s.integer(mmio.sbm);
s.integer(mmio.sw46);
s.integer(mmio.cbm);
s.integer(mmio.swen);
s.integer(mmio.cwen);
s.integer(mmio.bwp);
s.integer(mmio.siwp);
s.integer(mmio.ciwp);
s.integer(mmio.dmaen);
s.integer(mmio.dprio);
s.integer(mmio.cden);
s.integer(mmio.cdsel);
s.integer(mmio.dd);
s.integer(mmio.sd);
s.integer(mmio.chdend);
s.integer(mmio.dmasize);
s.integer(mmio.dmacb);
s.integer(mmio.dsa);
s.integer(mmio.dda);
s.integer(mmio.dtc);
s.integer(mmio.bbf);
s.array(mmio.brf);
s.integer(mmio.acm);
s.integer(mmio.md);
s.integer(mmio.ma);
s.integer(mmio.mb);
s.integer(mmio.hl);
s.integer(mmio.vb);
s.integer(mmio.va);
s.integer(mmio.vbit);
s.integer(mmio.cpu_irqfl);
s.integer(mmio.chdma_irqfl);
s.integer(mmio.sa1_irqfl);
s.integer(mmio.timer_irqfl);
s.integer(mmio.dma_irqfl);
s.integer(mmio.sa1_nmifl);
s.integer(mmio.hcr);
s.integer(mmio.vcr);
s.integer(mmio.mr);
s.integer(mmio.overflow);
}
#endif

View File

@@ -1,8 +1,11 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#define SDD1_CPP
#include "sdd1.hpp"
#define SDD1_CPP
namespace SNES {
SDD1 sdd1;
#include "serialization.cpp"
#include "sdd1emu.cpp"
void SDD1::init() {}
@@ -11,7 +14,7 @@ void SDD1::enable() {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::read()
for(unsigned i = 0x4300; i <= 0x437f; i++) {
cpu_mmio[i & 0x7f] = memory::mmio.get(i);
cpu_mmio[i & 0x7f] = memory::mmio.mmio[i - 0x2000];
memory::mmio.map(i, *this);
}
@@ -52,10 +55,10 @@ uint8 SDD1::mmio_read(unsigned addr) {
}
switch(addr) {
case 0x4804: return (mmc[0] >> 20) & 7;
case 0x4805: return (mmc[1] >> 20) & 7;
case 0x4806: return (mmc[2] >> 20) & 7;
case 0x4807: return (mmc[3] >> 20) & 7;
case 0x4804: return mmc[0] >> 20;
case 0x4805: return mmc[1] >> 20;
case 0x4806: return mmc[2] >> 20;
case 0x4807: return mmc[3] >> 20;
}
return cpu.regs.mdr;
@@ -81,10 +84,10 @@ void SDD1::mmio_write(unsigned addr, uint8 data) {
case 0x4800: sdd1_enable = data; break;
case 0x4801: xfer_enable = data; break;
case 0x4804: mmc[0] = (data & 7) << 20; break;
case 0x4805: mmc[1] = (data & 7) << 20; break;
case 0x4806: mmc[2] = (data & 7) << 20; break;
case 0x4807: mmc[3] = (data & 7) << 20; break;
case 0x4804: mmc[0] = data << 20; break;
case 0x4805: mmc[1] = data << 20; break;
case 0x4806: mmc[2] = data << 20; break;
case 0x4807: mmc[3] = data << 20; break;
}
}
@@ -150,9 +153,9 @@ void SDD1::write(unsigned addr, uint8 data) {
}
SDD1::SDD1() {
buffer.data = new uint8[65536];
}
SDD1::~SDD1() {
delete[] buffer.data;
}
};

View File

@@ -13,6 +13,7 @@ public:
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
SDD1();
~SDD1();
@@ -30,7 +31,7 @@ private:
SDD1emu sdd1emu;
struct {
uint8 *data; //pointer to decompressed S-DD1 data (65536 bytes)
uint8 data[65536]; //pointer to decompressed S-DD1 data
uint16 offset; //read index into S-DD1 decompression buffer
unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0
bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress()

View File

@@ -30,6 +30,7 @@ understood.
************************************************************************/
typedef uint8 bool8;
#define SDD1_read(__addr) (sdd1.read(__addr))
////////////////////////////////////////////////////

View File

@@ -28,7 +28,7 @@ understood.
************************************************************************/
typedef uint8_t bool8;
#define bool8 uint8
class SDD1_IM { //Input Manager
@@ -160,3 +160,5 @@ class SDD1emu {
SDD1_OL OL;
};
#undef bool8

View File

@@ -0,0 +1,19 @@
#ifdef SDD1_CPP
void SDD1::serialize(serializer &s) {
s.integer(sdd1_enable);
s.integer(xfer_enable);
s.array(mmc);
for(unsigned n = 0; n < 8; n++) {
s.integer(dma[n].addr);
s.integer(dma[n].size);
}
s.array(buffer.data);
s.integer(buffer.offset);
s.integer(buffer.size);
s.integer(buffer.ready);
}
#endif

View File

@@ -4,6 +4,7 @@ public:
void init(unsigned mode, unsigned offset, unsigned index);
void reset();
void serialize(serializer&);
SPC7110Decomp();
~SPC7110Decomp();

View File

@@ -0,0 +1,81 @@
#ifdef SPC7110_CPP
void SPC7110Decomp::serialize(serializer &s) {
s.integer(decomp_mode);
s.integer(decomp_offset);
s.array(decomp_buffer, decomp_buffer_size);
s.integer(decomp_buffer_rdoffset);
s.integer(decomp_buffer_wroffset);
s.integer(decomp_buffer_length);
for(unsigned n = 0; n < 32; n++) {
s.integer(context[n].index);
s.integer(context[n].invert);
}
}
void SPC7110::serialize(serializer &s) {
s.integer(r4801);
s.integer(r4802);
s.integer(r4803);
s.integer(r4804);
s.integer(r4805);
s.integer(r4806);
s.integer(r4807);
s.integer(r4808);
s.integer(r4809);
s.integer(r480a);
s.integer(r480b);
s.integer(r480c);
decomp.serialize(s);
s.integer(r4811);
s.integer(r4812);
s.integer(r4813);
s.integer(r4814);
s.integer(r4815);
s.integer(r4816);
s.integer(r4817);
s.integer(r4818);
s.integer(r481x);
s.integer(r4814_latch);
s.integer(r4815_latch);
s.integer(r4820);
s.integer(r4821);
s.integer(r4822);
s.integer(r4823);
s.integer(r4824);
s.integer(r4825);
s.integer(r4826);
s.integer(r4827);
s.integer(r4828);
s.integer(r4829);
s.integer(r482a);
s.integer(r482b);
s.integer(r482c);
s.integer(r482d);
s.integer(r482e);
s.integer(r482f);
s.integer(r4830);
s.integer(r4831);
s.integer(r4832);
s.integer(r4833);
s.integer(r4834);
s.integer(dx_offset);
s.integer(ex_offset);
s.integer(fx_offset);
s.integer(r4840);
s.integer(r4841);
s.integer(r4842);
s.integer(rtc_state);
s.integer(rtc_mode);
s.integer(rtc_index);
}
#endif

View File

@@ -1,8 +1,11 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#define SPC7110_CPP
#include "spc7110.hpp"
#define SPC7110_CPP
namespace SNES {
SPC7110 spc7110;
#include "serialization.cpp"
#include "decomp.cpp"
const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
@@ -532,17 +535,17 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
case 0x4831: {
r4831 = data;
dx_offset = datarom_addr((data & 7) * 0x100000);
dx_offset = datarom_addr(data * 0x100000);
} break;
case 0x4832: {
r4832 = data;
ex_offset = datarom_addr((data & 7) * 0x100000);
ex_offset = datarom_addr(data * 0x100000);
} break;
case 0x4833: {
r4833 = data;
fx_offset = datarom_addr((data & 7) * 0x100000);
fx_offset = datarom_addr(data * 0x100000);
} break;
case 0x4834: r4834 = data; break;
@@ -670,3 +673,5 @@ void SPC7110::write(unsigned addr, uint8 data) {
SPC7110::SPC7110() {
}
};

View File

@@ -45,6 +45,7 @@ public:
void decomp_init();
uint8 decomp_read();
void serialize(serializer&);
SPC7110();
private:
@@ -123,8 +124,10 @@ private:
uint8 r4841; //RTC index/data port
uint8 r4842; //RTC status
enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write } rtc_state;
enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c } rtc_mode;
enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write };
enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c };
unsigned rtc_state;
unsigned rtc_mode;
unsigned rtc_index;
static const unsigned months[12];

View File

@@ -0,0 +1,8 @@
#ifdef SRTC_CPP
void SRTC::serialize(serializer &s) {
s.integer(rtc_mode);
s.integer(rtc_index);
}
#endif

View File

@@ -1,6 +1,11 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#include "srtc.hpp"
#define SRTC_CPP
namespace SNES {
SRTC srtc;
#include "serialization.cpp"
const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
@@ -17,7 +22,7 @@ void SRTC::power() {
}
void SRTC::reset() {
rtc_mode = RTCM_Read;
rtc_mode = RtcRead;
rtc_index = -1;
update_time();
}
@@ -156,7 +161,7 @@ uint8 SRTC::mmio_read(unsigned addr) {
addr &= 0xffff;
if(addr == 0x2800) {
if(rtc_mode != RTCM_Read) return 0x00;
if(rtc_mode != RtcRead) return 0x00;
if(rtc_index < 0) {
update_time();
@@ -180,19 +185,19 @@ void SRTC::mmio_write(unsigned addr, uint8 data) {
data &= 0x0f; //only the low four bits are used
if(data == 0x0d) {
rtc_mode = RTCM_Read;
rtc_mode = RtcRead;
rtc_index = -1;
return;
}
if(data == 0x0e) {
rtc_mode = RTCM_Command;
rtc_mode = RtcCommand;
return;
}
if(data == 0x0f) return; //unknown behavior
if(rtc_mode == RTCM_Write) {
if(rtc_mode == RtcWrite) {
if(rtc_index >= 0 && rtc_index < 12) {
memory::cartrtc.write(rtc_index++, data);
@@ -206,17 +211,17 @@ void SRTC::mmio_write(unsigned addr, uint8 data) {
memory::cartrtc.write(rtc_index++, weekday(year, month, day));
}
}
} else if(rtc_mode == RTCM_Command) {
} else if(rtc_mode == RtcCommand) {
if(data == 0) {
rtc_mode = RTCM_Write;
rtc_mode = RtcWrite;
rtc_index = 0;
} else if(data == 4) {
rtc_mode = RTCM_Ready;
rtc_mode = RtcReady;
rtc_index = -1;
for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0);
} else {
//unknown behavior
rtc_mode = RTCM_Ready;
rtc_mode = RtcReady;
}
}
}
@@ -224,3 +229,5 @@ void SRTC::mmio_write(unsigned addr, uint8 data) {
SRTC::SRTC() {
}
};

View File

@@ -1,8 +1,5 @@
class SRTC : public MMIO {
public:
void update_time();
unsigned weekday(unsigned year, unsigned month, unsigned day);
void init();
void enable();
void power();
@@ -11,12 +8,17 @@ public:
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
void serialize(serializer&);
SRTC();
private:
static const unsigned months[12];
enum RTC_Mode { RTCM_Ready, RTCM_Command, RTCM_Read, RTCM_Write } rtc_mode;
enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite };
unsigned rtc_mode;
signed rtc_index;
void update_time();
unsigned weekday(unsigned year, unsigned month, unsigned day);
};
extern SRTC srtc;

View File

@@ -0,0 +1,7 @@
#ifdef ST010_CPP
void ST010::serialize(serializer &s) {
s.array(ram);
}
#endif

View File

@@ -1,10 +1,22 @@
#include <../base.hpp>
#define ST010_CPP
#include "st010.hpp"
#define ST010_CPP
namespace SNES {
ST010 st010;
#include "st010_data.hpp"
#include "serialization.cpp"
#include "st010_op.cpp"
void ST010::init() {
}
void ST010::enable() {
bus.map(Bus::MapDirect, 0x68, 0x6f, 0x0000, 0x0fff, *this);
bus.map(Bus::MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, *this);
}
int16 ST010::sin(int16 theta) {
return sin_table[(theta >> 8) & 0xff];
}
@@ -34,12 +46,12 @@ void ST010::writeb(uint16 addr, uint8 data) {
}
void ST010::writew(uint16 addr, uint16 data) {
writeb(addr + 0, data);
writeb(addr + 0, data >> 0);
writeb(addr + 1, data >> 8);
}
void ST010::writed(uint16 addr, uint32 data) {
writeb(addr + 0, data);
writeb(addr + 0, data >> 0);
writeb(addr + 1, data >> 8);
writeb(addr + 2, data >> 16);
writeb(addr + 3, data >> 24);
@@ -47,12 +59,6 @@ void ST010::writed(uint16 addr, uint32 data) {
//
void ST010::init() {
}
void ST010::enable() {
}
void ST010::power() {
reset();
}
@@ -85,3 +91,5 @@ void ST010::write(unsigned addr, uint8 data) {
ram[0x0021] &= ~0x80;
}
}
};

View File

@@ -8,6 +8,8 @@ public:
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
private:
uint8 ram[0x1000];
static const int16 sin_table[256];

View File

@@ -183,7 +183,7 @@ uint16 old_speed = speed;
else if(o1 > rot) { rot += 0x280; }
}
//turn of wrapping
//turn off wrapping
if(wrap) { rot -= 0x8000; }
//now check the distances (store for later)

20
src/chip/st011/st011.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include <../base.hpp>
#define ST011_CPP
namespace SNES {
ST011 st011;
void ST011::init() {
}
void ST011::enable() {
}
void ST011::power() {
}
void ST011::reset() {
}
};

9
src/chip/st011/st011.hpp Normal file
View File

@@ -0,0 +1,9 @@
class ST011 {
public:
void init();
void enable();
void power();
void reset();
};
extern ST011 st011;

124
src/chip/st018/st018.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include <../base.hpp>
#define ST018_CPP
namespace SNES {
ST018 st018;
uint8 ST018::mmio_read(unsigned addr) {
addr &= 0xffff;
if(addr == 0x3800) return regs.r3800;
if(addr == 0x3804) return regs.r3804;
return cpu.regs.mdr;
}
void ST018::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if(addr == 0x3802) {
switch(regs.mode) {
case Waiting: {
switch(data) {
case 0x01: regs.r3800 = regs.r3800_01; break;
case 0xaa: op_board_upload(); break;
case 0xb2: op_b2(); break;
case 0xb3: op_b3(); break;
case 0xb4: op_b4(); break;
case 0xb5: op_b5(); break;
case 0xf1: op_query_chip(); break;
case 0xf2: op_query_chip(); break;
default: fprintf(stdout, "* ST018 w3802::%.2x\n", data); break;
}
} return;
case BoardUpload: {
op_board_upload(data);
} return;
}
}
if(addr == 0x3804) {
regs.w3804 <<= 8;
regs.w3804 |= data;
regs.w3804 &= 0xffffff;
return;
}
}
void ST018::init() {
}
void ST018::enable() {
for(unsigned i = 0x3800; i <= 0x38ff; i++) memory::mmio.map(i, *this);
}
void ST018::power() {
reset();
}
void ST018::reset() {
regs.mode = Waiting;
regs.r3800 = 0x00;
regs.r3804 = 0x85;
regs.w3804 = 0;
for(unsigned i = 0; i < 97; i++) board[i] = 0;
}
//=============
//ST018 opcodes
//=============
void ST018::op_board_upload() {
regs.mode = BoardUpload;
regs.counter = 0;
regs.r3800 = 0xe0;
}
void ST018::op_board_upload(uint8 data) {
board[regs.counter] = data;
regs.r3800 = 96 - regs.counter;
regs.counter++;
if(regs.counter >= 97) {
regs.mode = Waiting;
#if 0
for(unsigned y = 0; y < 9; y++) {
for(unsigned x = 0; x < 9; x++) {
fprintf(stdout, "%.2x ", board[y * 9 + x]);
}
fprintf(stdout, "\n");
}
for(unsigned n = 0; n < 16; n++) fprintf(stdout, "%.2x ", board[81 + n]);
fprintf(stdout, "\n\n");
#endif
}
}
void ST018::op_b2() {
fprintf(stdout, "* ST018 w3802::b2\n");
regs.r3800 = 0xe0;
regs.r3800_01 = 0; //unknown
}
void ST018::op_b3() {
fprintf(stdout, "* ST018 w3802::b3\n");
regs.r3800 = 0xe0;
regs.r3800_01 = 1; //0 = player lost?
}
void ST018::op_b4() {
fprintf(stdout, "* ST018 w3802::b4\n");
regs.r3800 = 0xe0;
regs.r3800_01 = 1; //0 = player won?
}
void ST018::op_b5() {
fprintf(stdout, "* ST018 w3802::b5\n");
regs.r3800 = 0xe0;
regs.r3800_01 = 0; //1 = move will result in checkmate?
}
void ST018::op_query_chip() {
regs.r3800 = 0x00;
}
};

51
src/chip/st018/st018.hpp Normal file
View File

@@ -0,0 +1,51 @@
class ST018 : public MMIO {
public:
void init();
void enable();
void power();
void reset();
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
enum mode_t { Waiting, BoardUpload };
struct regs_t {
mode_t mode;
uint8 r3800;
uint8 r3800_01;
uint8 r3804;
unsigned w3804;
unsigned counter;
} regs;
enum PieceID {
Pawn = 0x00, //foot soldier
Lance = 0x04, //incense chariot
Knight = 0x08, //cassia horse
Silver = 0x0c, //silver general
Gold = 0x10, //gold general
Rook = 0x14, //flying chariot
Bishop = 0x18, //angle mover
King = 0x1c, //king
};
enum PieceFlag {
PlayerA = 0x20,
PlayerB = 0x40,
};
uint8 board[9 * 9 + 16];
private:
void op_board_upload();
void op_board_upload(uint8 data);
void op_b2();
void op_b3();
void op_b4();
void op_b5();
void op_query_chip();
};
extern ST018 st018;

View File

@@ -0,0 +1,106 @@
#ifdef SUPERFX_CPP
SuperFXBus superfxbus;
namespace memory {
SuperFXGSUROM gsurom;
SuperFXGSURAM gsuram;
SuperFXCPUROM fxrom;
SuperFXCPURAM fxram;
}
void SuperFXBus::init() {
map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped);
map(MapLinear, 0x00, 0x3f, 0x0000, 0x7fff, memory::gsurom);
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::gsurom);
map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::gsurom);
map(MapLinear, 0x60, 0x7f, 0x0000, 0xffff, memory::gsuram);
bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::fxram, 0x0000, 0x2000);
bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::fxrom);
bus.map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::fxrom);
bus.map(MapLinear, 0x60, 0x7d, 0x0000, 0xffff, memory::fxram);
bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::fxram, 0x0000, 0x2000);
bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::fxrom);
bus.map(MapLinear, 0xc0, 0xdf, 0x0000, 0xffff, memory::fxrom);
bus.map(MapLinear, 0xe0, 0xff, 0x0000, 0xffff, memory::fxram);
}
//ROM / RAM access from the SuperFX CPU
unsigned SuperFXGSUROM::size() const {
return memory::cartrom.size();
}
uint8 SuperFXGSUROM::read(unsigned addr) {
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) {
superfx.add_clocks(6);
scheduler.sync_copcpu();
}
return memory::cartrom.read(addr);
}
void SuperFXGSUROM::write(unsigned addr, uint8 data) {
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) {
superfx.add_clocks(6);
scheduler.sync_copcpu();
}
memory::cartrom.write(addr, data);
}
unsigned SuperFXGSURAM::size() const {
return memory::cartram.size();
}
uint8 SuperFXGSURAM::read(unsigned addr) {
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) {
superfx.add_clocks(6);
scheduler.sync_copcpu();
}
return memory::cartram.read(addr);
}
void SuperFXGSURAM::write(unsigned addr, uint8 data) {
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) {
superfx.add_clocks(6);
scheduler.sync_copcpu();
}
memory::cartram.write(addr, data);
}
//ROM / RAM access from the S-CPU
unsigned SuperFXCPUROM::size() const {
return memory::cartrom.size();
}
uint8 SuperFXCPUROM::read(unsigned addr) {
if(superfx.regs.sfr.g && superfx.regs.scmr.ron) {
static const uint8_t data[16] = {
0x00, 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, 0x01,
0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x0c, 0x01,
};
return data[addr & 15];
}
return memory::cartrom.read(addr);
}
void SuperFXCPUROM::write(unsigned addr, uint8 data) {
memory::cartrom.write(addr, data);
}
unsigned SuperFXCPURAM::size() const {
return memory::cartram.size();
}
uint8 SuperFXCPURAM::read(unsigned addr) {
if(superfx.regs.sfr.g && superfx.regs.scmr.ran) return cpu.regs.mdr;
return memory::cartram.read(addr);
}
void SuperFXCPURAM::write(unsigned addr, uint8 data) {
memory::cartram.write(addr, data);
}
#endif

View File

@@ -0,0 +1,34 @@
struct SuperFXBus : Bus {
void init();
};
struct SuperFXGSUROM : Memory {
unsigned size() const;
uint8 read(unsigned);
void write(unsigned, uint8);
};
struct SuperFXGSURAM : Memory {
unsigned size() const;
uint8 read(unsigned);
void write(unsigned, uint8);
};
struct SuperFXCPUROM : Memory {
unsigned size() const;
uint8 read(unsigned);
void write(unsigned, uint8);
};
struct SuperFXCPURAM : Memory {
unsigned size() const;
uint8 read(unsigned);
void write(unsigned, uint8);
};
namespace memory {
extern SuperFXGSUROM gsurom;
extern SuperFXGSURAM gsuram;
extern SuperFXCPUROM fxrom;
extern SuperFXCPURAM fxram;
}

View File

@@ -0,0 +1,107 @@
#ifdef SUPERFX_CPP
#include "opcodes.cpp"
#include "opcode_table.cpp"
uint8 SuperFX::color(uint8 source) {
if(regs.por.highnibble) return (regs.colr & 0xf0) | (source >> 4);
if(regs.por.freezehigh) return (regs.colr & 0xf0) | (source & 0x0f);
return source;
}
void SuperFX::plot(uint8 x, uint8 y) {
uint8 color = regs.colr;
if(regs.por.dither && regs.scmr.md != 3) {
if((x ^ y) & 1) color >>= 4;
color &= 0x0f;
}
if(!regs.por.transparent) {
if(regs.scmr.md == 3) {
if(regs.por.freezehigh) {
if((color & 0x0f) == 0) return;
} else {
if(color == 0) return;
}
} else {
if((color & 0x0f) == 0) return;
}
}
uint16 offset = (y << 5) + (x >> 3);
if(offset != pixelcache[0].offset) {
pixelcache_flush(pixelcache[1]);
pixelcache[1] = pixelcache[0];
pixelcache[0].bitpend = 0x00;
pixelcache[0].offset = offset;
}
x = (x & 7) ^ 7;
pixelcache[0].data[x] = color;
pixelcache[0].bitpend |= 1 << x;
if(pixelcache[0].bitpend == 0xff) {
pixelcache_flush(pixelcache[1]);
pixelcache[1] = pixelcache[0];
pixelcache[0].bitpend = 0x00;
}
}
uint8 SuperFX::rpix(uint8 x, uint8 y) {
pixelcache_flush(pixelcache[1]);
pixelcache_flush(pixelcache[0]);
unsigned cn; //character number
switch(regs.por.obj ? 3 : regs.scmr.ht) {
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break;
case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break;
}
unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 };
unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2);
uint8 data = 0x00;
x = (x & 7) ^ 7;
for(unsigned n = 0; n < bpp; n++) {
unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 };
add_clocks(memory_access_speed);
data |= ((superfxbus.read(addr + byte) >> x) & 1) << n;
}
return data;
}
void SuperFX::pixelcache_flush(pixelcache_t &cache) {
if(cache.bitpend == 0x00) return;
uint8 x = cache.offset << 3;
uint8 y = cache.offset >> 5;
unsigned cn; //character number
switch(regs.por.obj ? 3 : regs.scmr.ht) {
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break;
case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break;
}
unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 };
unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2);
for(unsigned n = 0; n < bpp; n++) {
unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 };
uint8 data = 0x00;
for(unsigned x = 0; x < 8; x++) data |= ((cache.data[x] >> n) & 1) << x;
if(cache.bitpend != 0xff) {
add_clocks(memory_access_speed);
data &= cache.bitpend;
data |= superfxbus.read(addr + byte) & ~cache.bitpend;
}
add_clocks(memory_access_speed);
superfxbus.write(addr + byte, data);
}
cache.bitpend = 0x00;
}
#endif

View File

@@ -0,0 +1,92 @@
#include "registers.hpp"
uint8 color(uint8 source);
void plot(uint8 x, uint8 y);
uint8 rpix(uint8 x, uint8 y);
void pixelcache_flush(pixelcache_t &cache);
void (SuperFX::*opcode_table[1024])();
void initialize_opcode_table();
//opcodes.cpp
template<int> void op_adc_i();
template<int> void op_adc_r();
template<int> void op_add_i();
template<int> void op_add_r();
void op_alt1();
void op_alt2();
void op_alt3();
template<int> void op_and_i();
template<int> void op_and_r();
void op_asr();
void op_bge();
void op_bcc();
void op_bcs();
void op_beq();
template<int> void op_bic_i();
template<int> void op_bic_r();
void op_blt();
void op_bmi();
void op_bne();
void op_bpl();
void op_bra();
void op_bvc();
void op_bvs();
void op_cache();
void op_cmode();
template<int> void op_cmp_r();
void op_color();
template<int> void op_dec_r();
void op_div2();
void op_fmult();
template<int> void op_from_r();
void op_getb();
void op_getbl();
void op_getbh();
void op_getbs();
void op_getc();
void op_hib();
template<int> void op_ibt_r();
template<int> void op_inc_r();
template<int> void op_iwt_r();
template<int> void op_jmp_r();
template<int> void op_ldb_ir();
template<int> void op_ldw_ir();
template<int> void op_link();
template<int> void op_ljmp_r();
template<int> void op_lm_r();
template<int> void op_lms_r();
void op_lmult();
void op_lob();
void op_loop();
void op_lsr();
void op_merge();
template<int> void op_mult_i();
template<int> void op_mult_r();
void op_nop();
void op_not();
template<int> void op_or_i();
template<int> void op_or_r();
void op_plot();
void op_ramb();
void op_rol();
void op_romb();
void op_ror();
void op_rpix();
template<int> void op_sbc_r();
void op_sbk();
void op_sex();
template<int> void op_sm_r();
template<int> void op_sms_r();
template<int> void op_stb_ir();
void op_stop();
template<int> void op_stw_ir();
template<int> void op_sub_i();
template<int> void op_sub_r();
void op_swap();
template<int> void op_to_r();
template<int> void op_umult_i();
template<int> void op_umult_r();
template<int> void op_with_r();
template<int> void op_xor_i();
template<int> void op_xor_r();

View File

@@ -0,0 +1,270 @@
#ifdef SUPERFX_CPP
void SuperFX::initialize_opcode_table() {
#define op4(id, name) \
op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>)
#define op6(id, name) \
op(id+ 0, name< 8>) op(id+ 1, name< 9>) op(id+ 2, name<10>) op(id+ 3, name<11>) \
op(id+ 4, name<12>) op(id+ 5, name<13>)
#define op12(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>)
#define op15l(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \
op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>)
#define op15h(id, name) \
op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) \
op(id+ 4, name< 5>) op(id+ 5, name< 6>) op(id+ 6, name< 7>) op(id+ 7, name< 8>) \
op(id+ 8, name< 9>) op(id+ 9, name<10>) op(id+10, name<11>) op(id+11, name<12>) \
op(id+12, name<13>) op(id+13, name<14>) op(id+14, name<15>)
#define op16(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \
op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) op(id+15, name<15>)
//======
// ALT0
//======
#define op(id, name) opcode_table[ 0 + id] = &SuperFX::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stw_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldw_ir)
op (0x4c, plot)
op (0x4d, swap)
op (0x4e, color)
op (0x4f, not)
op16 (0x50, add_r)
op16 (0x60, sub_r)
op (0x70, merge)
op15h(0x71, and_r)
op16 (0x80, mult_r)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, asr)
op (0x97, ror)
op6 (0x98, jmp_r)
op (0x9e, lob)
op (0x9f, fmult)
op16 (0xa0, ibt_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, or_r)
op15l(0xd0, inc_r)
op (0xdf, getc)
op15l(0xe0, dec_r)
op (0xef, getb)
op16 (0xf0, iwt_r)
#undef op
//======
// ALT1
//======
#define op(id, name) opcode_table[256 + id] = &SuperFX::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stb_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldb_ir)
op (0x4c, rpix)
op (0x4d, swap)
op (0x4e, cmode)
op (0x4f, not)
op16 (0x50, adc_r)
op16 (0x60, sbc_r)
op (0x70, merge)
op15h(0x71, bic_r)
op16 (0x80, umult_r)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, div2)
op (0x97, ror)
op6 (0x98, ljmp_r)
op (0x9e, lob)
op (0x9f, lmult)
op16 (0xa0, lms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, xor_r)
op15l(0xd0, inc_r)
op (0xdf, getc)
op15l(0xe0, dec_r)
op (0xef, getbh)
op16 (0xf0, lm_r)
#undef op
//======
// ALT2
//======
#define op(id, name) opcode_table[512 + id] = &SuperFX::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stw_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldw_ir)
op (0x4c, plot)
op (0x4d, swap)
op (0x4e, color)
op (0x4f, not)
op16 (0x50, add_i)
op16 (0x60, sub_i)
op (0x70, merge)
op15h(0x71, and_i)
op16 (0x80, mult_i)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, asr)
op (0x97, ror)
op6 (0x98, jmp_r)
op (0x9e, lob)
op (0x9f, fmult)
op16 (0xa0, sms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, or_i)
op15l(0xd0, inc_r)
op (0xdf, ramb)
op15l(0xe0, dec_r)
op (0xef, getbl)
op16 (0xf0, sm_r)
#undef op
//======
// ALT3
//======
#define op(id, name) opcode_table[768 + id] = &SuperFX::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stb_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldb_ir)
op (0x4c, rpix)
op (0x4d, swap)
op (0x4e, cmode)
op (0x4f, not)
op16 (0x50, adc_i)
op16 (0x60, cmp_r)
op (0x70, merge)
op15h(0x71, bic_i)
op16 (0x80, umult_i)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, div2)
op (0x97, ror)
op6 (0x98, ljmp_r)
op (0x9e, lob)
op (0x9f, lmult)
op16 (0xa0, lms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, xor_i)
op15l(0xd0, inc_r)
op (0xdf, romb)
op15l(0xe0, dec_r)
op (0xef, getbs)
op16 (0xf0, lm_r)
#undef op
#undef op4
#undef op6
#undef op12
#undef op15l
#undef op15h
#undef op16
}
#endif

View File

@@ -0,0 +1,661 @@
#ifdef SUPERFX_CPP
//$00 stop
void SuperFX::op_stop() {
if(regs.cfgr.irq == 0) {
regs.sfr.irq = 1;
cpu.regs.irq = 1;
}
regs.sfr.g = 0;
regs.pipeline = 0x01;
regs.reset();
}
//$01 nop
void SuperFX::op_nop() {
regs.reset();
}
//$02 cache
void SuperFX::op_cache() {
if(regs.cbr != (regs.r[15] & 0xfff0)) {
regs.cbr = regs.r[15] & 0xfff0;
cache_flush();
}
regs.reset();
}
//$03 lsr
void SuperFX::op_lsr() {
regs.sfr.cy = (regs.sr() & 1);
regs.dr() = regs.sr() >> 1;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$04 rol
void SuperFX::op_rol() {
bool carry = (regs.sr() & 0x8000);
regs.dr() = (regs.sr() << 1) | regs.sfr.cy;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = carry;
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$05 bra e
void SuperFX::op_bra() {
regs.r[15] += (int8)pipe();
}
//$06 blt e
void SuperFX::op_blt() {
int e = (int8)pipe();
if((regs.sfr.s ^ regs.sfr.ov) == 0) regs.r[15] += e;
}
//$07 bge e
void SuperFX::op_bge() {
int e = (int8)pipe();
if((regs.sfr.s ^ regs.sfr.ov) == 1) regs.r[15] += e;
}
//$08 bne e
void SuperFX::op_bne() {
int e = (int8)pipe();
if(regs.sfr.z == 0) regs.r[15] += e;
}
//$09 beq e
void SuperFX::op_beq() {
int e = (int8)pipe();
if(regs.sfr.z == 1) regs.r[15] += e;
}
//$0a bpl e
void SuperFX::op_bpl() {
int e = (int8)pipe();
if(regs.sfr.s == 0) regs.r[15] += e;
}
//$0b bmi e
void SuperFX::op_bmi() {
int e = (int8)pipe();
if(regs.sfr.s == 1) regs.r[15] += e;
}
//$0c bcc e
void SuperFX::op_bcc() {
int e = (int8)pipe();
if(regs.sfr.cy == 0) regs.r[15] += e;
}
//$0d bcs e
void SuperFX::op_bcs() {
int e = (int8)pipe();
if(regs.sfr.cy == 1) regs.r[15] += e;
}
//$0e bvc e
void SuperFX::op_bvc() {
int e = (int8)pipe();
if(regs.sfr.ov == 0) regs.r[15] += e;
}
//$0f bvs e
void SuperFX::op_bvs() {
int e = (int8)pipe();
if(regs.sfr.ov == 1) regs.r[15] += e;
}
//$10-1f(b0): to rN
//$10-1f(b1): move rN
template<int n> void SuperFX::op_to_r() {
if(regs.sfr.b == 0) {
regs.dreg = n;
} else {
regs.r[n] = regs.sr();
regs.reset();
}
}
//$20-2f: with rN
template<int n> void SuperFX::op_with_r() {
regs.sreg = n;
regs.dreg = n;
regs.sfr.b = 1;
}
//$30-3b(alt0): stw (rN)
template<int n> void SuperFX::op_stw_ir() {
regs.ramaddr = regs.r[n];
rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8);
regs.reset();
}
//$30-3b(alt1): stb (rN)
template<int n> void SuperFX::op_stb_ir() {
regs.ramaddr = regs.r[n];
rambuffer_write(regs.ramaddr, regs.sr());
regs.reset();
}
//$3c loop
void SuperFX::op_loop() {
regs.r[12]--;
regs.sfr.s = (regs.r[12] & 0x8000);
regs.sfr.z = (regs.r[12] == 0);
if(!regs.sfr.z) regs.r[15] = regs.r[13];
regs.reset();
}
//$3d alt1
void SuperFX::op_alt1() {
regs.sfr.b = 0;
regs.sfr.alt1 = 1;
}
//$3e alt2
void SuperFX::op_alt2() {
regs.sfr.b = 0;
regs.sfr.alt2 = 1;
}
//$3f alt3
void SuperFX::op_alt3() {
regs.sfr.b = 0;
regs.sfr.alt1 = 1;
regs.sfr.alt2 = 1;
}
//$40-4b(alt0): ldw (rN)
template<int n> void SuperFX::op_ldw_ir() {
regs.ramaddr = regs.r[n];
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.dr() = data;
regs.reset();
}
//$40-4b(alt1): ldb (rN)
template<int n> void SuperFX::op_ldb_ir() {
regs.ramaddr = regs.r[n];
regs.dr() = rambuffer_read(regs.ramaddr);
regs.reset();
}
//$4c(alt0): plot
void SuperFX::op_plot() {
plot(regs.r[1], regs.r[2]);
regs.r[1]++;
regs.reset();
}
//$4c(alt1): rpix
void SuperFX::op_rpix() {
regs.dr() = rpix(regs.r[1], regs.r[2]);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$4d: swap
void SuperFX::op_swap() {
regs.dr() = (regs.sr() >> 8) | (regs.sr() << 8);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$4e(alt0): color
void SuperFX::op_color() {
regs.colr = color(regs.sr());
regs.reset();
}
//$4e(alt1): cmode
void SuperFX::op_cmode() {
regs.por = regs.sr();
regs.reset();
}
//$4f: not
void SuperFX::op_not() {
regs.dr() = ~regs.sr();
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$50-5f(alt0): add rN
template<int n> void SuperFX::op_add_r() {
int r = regs.sr() + regs.r[n];
regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt1): adc rN
template<int n> void SuperFX::op_adc_r() {
int r = regs.sr() + regs.r[n] + regs.sfr.cy;
regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt2): add #N
template<int n> void SuperFX::op_add_i() {
int r = regs.sr() + n;
regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt3): adc #N
template<int n> void SuperFX::op_adc_i() {
int r = regs.sr() + n + regs.sfr.cy;
regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt0): sub rN
template<int n> void SuperFX::op_sub_r() {
int r = regs.sr() - regs.r[n];
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt1): sbc rN
template<int n> void SuperFX::op_sbc_r() {
int r = regs.sr() - regs.r[n] - !regs.sfr.cy;
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt2): sub #N
template<int n> void SuperFX::op_sub_i() {
int r = regs.sr() - n;
regs.sfr.ov = (regs.sr() ^ n) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt3): cmp rN
template<int n> void SuperFX::op_cmp_r() {
int r = regs.sr() - regs.r[n];
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.reset();
}
//$70: merge
void SuperFX::op_merge() {
regs.dr() = (regs.r[7] & 0xff00) | (regs.r[8] >> 8);
regs.sfr.ov = (regs.dr() & 0xc0c0);
regs.sfr.s = (regs.dr() & 0x8080);
regs.sfr.cy = (regs.dr() & 0xe0e0);
regs.sfr.z = (regs.dr() & 0xf0f0);
regs.reset();
}
//$71-7f(alt0): and rN
template<int n> void SuperFX::op_and_r() {
regs.dr() = regs.sr() & regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt1): bic rN
template<int n> void SuperFX::op_bic_r() {
regs.dr() = regs.sr() & ~regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt2): and #N
template<int n> void SuperFX::op_and_i() {
regs.dr() = regs.sr() & n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt3): bic #N
template<int n> void SuperFX::op_bic_i() {
regs.dr() = regs.sr() & ~n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$80-8f(alt0): mult rN
template<int n> void SuperFX::op_mult_r() {
regs.dr() = (int8)regs.sr() * (int8)regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) add_clocks(2);
}
//$80-8f(alt1): umult rN
template<int n> void SuperFX::op_umult_r() {
regs.dr() = (uint8)regs.sr() * (uint8)regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) add_clocks(2);
}
//$80-8f(alt2): mult #N
template<int n> void SuperFX::op_mult_i() {
regs.dr() = (int8)regs.sr() * (int8)n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) add_clocks(2);
}
//$80-8f(alt3): umult #N
template<int n> void SuperFX::op_umult_i() {
regs.dr() = (uint8)regs.sr() * (uint8)n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) add_clocks(2);
}
//$90: sbk
void SuperFX::op_sbk() {
rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8);
regs.reset();
}
//$91-94: link #N
template<int n> void SuperFX::op_link() {
regs.r[11] = regs.r[15] + n;
regs.reset();
}
//$95: sex
void SuperFX::op_sex() {
regs.dr() = (int8)regs.sr();
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$96(alt0): asr
void SuperFX::op_asr() {
regs.sfr.cy = (regs.sr() & 1);
regs.dr() = (int16_t)regs.sr() >> 1;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$96(alt1): div2
void SuperFX::op_div2() {
regs.sfr.cy = (regs.sr() & 1);
regs.dr() = ((int16_t)regs.sr() >> 1) + ((regs.sr() + 1) >> 16);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$97: ror
void SuperFX::op_ror() {
bool carry = (regs.sr() & 1);
regs.dr() = (regs.sfr.cy << 15) | (regs.sr() >> 1);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = carry;
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$98-9d(alt0): jmp rN
template<int n> void SuperFX::op_jmp_r() {
regs.r[15] = regs.r[n];
regs.reset();
}
//$98-9d(alt1): ljmp rN
template<int n> void SuperFX::op_ljmp_r() {
regs.pbr = regs.r[n] & 0x7f;
regs.r[15] = regs.sr();
regs.cbr = regs.r[15] & 0xfff0;
cache_flush();
regs.reset();
}
//$9e: lob
void SuperFX::op_lob() {
regs.dr() = regs.sr() & 0xff;
regs.sfr.s = (regs.dr() & 0x80);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$9f(alt0): fmult
void SuperFX::op_fmult() {
uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6];
regs.dr() = result >> 16;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = (result & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
add_clocks(4 + (regs.cfgr.ms0 << 2));
}
//$9f(alt1): lmult
void SuperFX::op_lmult() {
uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6];
regs.r[4] = result;
regs.dr() = result >> 16;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = (result & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
add_clocks(4 + (regs.cfgr.ms0 << 2));
}
//$a0-af(alt0): ibt rN,#pp
template<int n> void SuperFX::op_ibt_r() {
regs.r[n] = (int8)pipe();
regs.reset();
}
//$a0-af(alt1): lms rN,(yy)
template<int n> void SuperFX::op_lms_r() {
regs.ramaddr = pipe() << 1;
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.r[n] = data;
regs.reset();
}
//$a0-af(alt2): sms (yy),rN
template<int n> void SuperFX::op_sms_r() {
regs.ramaddr = pipe() << 1;
rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8);
regs.reset();
}
//$b0-bf(b0): from rN
//$b0-bf(b1): moves rN
template<int n> void SuperFX::op_from_r() {
if(regs.sfr.b == 0) {
regs.sreg = n;
} else {
regs.dr() = regs.r[n];
regs.sfr.ov = (regs.dr() & 0x80);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
}
//$c0: hib
void SuperFX::op_hib() {
regs.dr() = regs.sr() >> 8;
regs.sfr.s = (regs.dr() & 0x80);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt0): or rN
template<int n> void SuperFX::op_or_r() {
regs.dr() = regs.sr() | regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt1): xor rN
template<int n> void SuperFX::op_xor_r() {
regs.dr() = regs.sr() ^ regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt2): or #N
template<int n> void SuperFX::op_or_i() {
regs.dr() = regs.sr() | n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt3): xor #N
template<int n> void SuperFX::op_xor_i() {
regs.dr() = regs.sr() ^ n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$d0-de: inc rN
template<int n> void SuperFX::op_inc_r() {
regs.r[n]++;
regs.sfr.s = (regs.r[n] & 0x8000);
regs.sfr.z = (regs.r[n] == 0);
regs.reset();
}
//$df(alt0): getc
void SuperFX::op_getc() {
regs.colr = color(rombuffer_read());
regs.reset();
}
//$df(alt2): ramb
void SuperFX::op_ramb() {
rambuffer_sync();
regs.rambr = regs.sr();
regs.reset();
}
//$df(alt3): romb
void SuperFX::op_romb() {
rombuffer_sync();
regs.rombr = regs.sr() & 0x7f;
regs.reset();
}
//$e0-ee: dec rN
template<int n> void SuperFX::op_dec_r() {
regs.r[n]--;
regs.sfr.s = (regs.r[n] & 0x8000);
regs.sfr.z = (regs.r[n] == 0);
regs.reset();
}
//$ef(alt0): getb
void SuperFX::op_getb() {
regs.dr() = rombuffer_read();
regs.reset();
}
//$ef(alt1): getbh
void SuperFX::op_getbh() {
regs.dr() = (rombuffer_read() << 8) | (regs.sr() & 0x00ff);
regs.reset();
}
//$ef(alt2): getbl
void SuperFX::op_getbl() {
regs.dr() = (regs.sr() & 0xff00) | (rombuffer_read() << 0);
regs.reset();
}
//$ef(alt3): getbs
void SuperFX::op_getbs() {
regs.dr() = (int8)rombuffer_read();
regs.reset();
}
//$f0-ff(alt0): iwt rN,#xx
template<int n> void SuperFX::op_iwt_r() {
uint16_t data;
data = pipe() << 0;
data |= pipe() << 8;
regs.r[n] = data;
regs.reset();
}
//$f0-ff(alt1): lm rN,(xx)
template<int n> void SuperFX::op_lm_r() {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.r[n] = data;
regs.reset();
}
//$f0-ff(alt2): sm (xx),rN
template<int n> void SuperFX::op_sm_r() {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8);
regs.reset();
}
#endif

View File

@@ -0,0 +1,175 @@
//accepts a callback binding so r14 writes can trigger ROM buffering transparently
struct reg16_t : noncopyable {
uint16 data;
function<void (uint16)> on_modify;
inline operator unsigned() const { return data; }
inline uint16 assign(uint16 i) {
if(on_modify) on_modify(i);
else data = i;
return data;
}
inline unsigned operator++() { return assign(data + 1); }
inline unsigned operator--() { return assign(data - 1); }
inline unsigned operator++(int) { unsigned r = data; assign(data + 1); return r; }
inline unsigned operator--(int) { unsigned r = data; assign(data - 1); return r; }
inline unsigned operator = (unsigned i) { return assign(i); }
inline unsigned operator |= (unsigned i) { return assign(data | i); }
inline unsigned operator ^= (unsigned i) { return assign(data ^ i); }
inline unsigned operator &= (unsigned i) { return assign(data & i); }
inline unsigned operator <<= (unsigned i) { return assign(data << i); }
inline unsigned operator >>= (unsigned i) { return assign(data >> i); }
inline unsigned operator += (unsigned i) { return assign(data + i); }
inline unsigned operator -= (unsigned i) { return assign(data - i); }
inline unsigned operator *= (unsigned i) { return assign(data * i); }
inline unsigned operator /= (unsigned i) { return assign(data / i); }
inline unsigned operator %= (unsigned i) { return assign(data % i); }
inline unsigned operator = (const reg16_t& i) { return assign(i); }
reg16_t() : data(0) {}
};
struct sfr_t {
bool irq; //interrupt flag
bool b; //WITH flag
bool ih; //immediate higher 8-bit flag
bool il; //immediate lower 8-bit flag
bool alt2; //ALT2 mode
bool alt1; //ALT2 instruction mode
bool r; //ROM r14 read flag
bool g; //GO flag
bool ov; //overflow flag
bool s; //sign flag
bool cy; //carry flag
bool z; //zero flag
operator unsigned() const {
return (irq << 15) | (b << 12) | (ih << 11) | (il << 10) | (alt2 << 9) | (alt1 << 8)
| (r << 6) | (g << 5) | (ov << 4) | (s << 3) | (cy << 2) | (z << 1);
}
sfr_t& operator=(uint16_t data) {
irq = data & 0x8000;
b = data & 0x1000;
ih = data & 0x0800;
il = data & 0x0400;
alt2 = data & 0x0200;
alt1 = data & 0x0100;
r = data & 0x0040;
g = data & 0x0020;
ov = data & 0x0010;
s = data & 0x0008;
cy = data & 0x0004;
z = data & 0x0002;
return *this;
}
};
struct scmr_t {
unsigned ht;
bool ron;
bool ran;
unsigned md;
operator unsigned() const {
return ((ht >> 1) << 5) | (ron << 4) | (ran << 3) | ((ht & 1) << 2) | (md);
}
scmr_t& operator=(uint8 data) {
ht = (bool)(data & 0x20) << 1;
ht |= (bool)(data & 0x04) << 0;
ron = data & 0x10;
ran = data & 0x08;
md = data & 0x03;
return *this;
}
};
struct por_t {
bool obj;
bool freezehigh;
bool highnibble;
bool dither;
bool transparent;
operator unsigned() const {
return (obj << 4) | (freezehigh << 3) | (highnibble << 2) | (dither << 1) | (transparent);
}
por_t& operator=(uint8 data) {
obj = data & 0x10;
freezehigh = data & 0x08;
highnibble = data & 0x04;
dither = data & 0x02;
transparent = data & 0x01;
return *this;
}
};
struct cfgr_t {
bool irq;
bool ms0;
operator unsigned() const {
return (irq << 7) | (ms0 << 5);
}
cfgr_t& operator=(uint8 data) {
irq = data & 0x80;
ms0 = data & 0x20;
return *this;
}
};
struct regs_t {
uint8 pipeline;
uint16 ramaddr;
reg16_t r[16]; //general purpose registers
sfr_t sfr; //status flag register
uint8 pbr; //program bank register
uint8 rombr; //game pack ROM bank register
bool rambr; //game pack RAM bank register
uint16 cbr; //cache base register
uint8 scbr; //screen base register
scmr_t scmr; //screen mode register
uint8 colr; //color register
por_t por; //plot option register
bool bramr; //back-up RAM register
uint8 vcr; //version code register
cfgr_t cfgr; //config register
bool clsr; //clock select register
unsigned romcl; //clock ticks until romdr is valid
uint8 romdr; //ROM buffer data register
unsigned ramcl; //clock ticks until ramdr is valid
uint16 ramar; //RAM buffer address register
uint8 ramdr; //RAM buffer data register
unsigned sreg, dreg;
reg16_t& sr() { return r[sreg]; } //source register (from)
reg16_t& dr() { return r[dreg]; } //destination register (to)
void reset() {
sfr.b = 0;
sfr.alt1 = 0;
sfr.alt2 = 0;
sreg = 0;
dreg = 0;
}
} regs;
struct cache_t {
uint8 buffer[512];
bool valid[32];
} cache;
struct pixelcache_t {
uint16 offset;
uint8 bitpend;
uint8 data[8];
} pixelcache[2];

View File

@@ -0,0 +1,279 @@
#ifdef SUPERFX_CPP
void SuperFX::disassemble_opcode(char *output) {
*output = 0;
if(!regs.sfr.alt2) {
if(!regs.sfr.alt1) {
disassemble_alt0(output);
} else {
disassemble_alt1(output);
}
} else {
if(!regs.sfr.alt1) {
disassemble_alt2(output);
} else {
disassemble_alt3(output);
}
}
unsigned length = strlen(output);
while(length++ < 20) strcat(output, " ");
}
#define case4(id) \
case id+ 0: case id+ 1: case id+ 2: case id+ 3
#define case6(id) \
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5
#define case12(id) \
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
case id+ 8: case id+ 9: case id+10: case id+11
#define case15(id) \
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14
#define case16(id) \
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15
#define op0 regs.pipeline
#define op1 superfxbus.read((regs.pbr << 16) + regs.r[15] + 0)
#define op2 superfxbus.read((regs.pbr << 16) + regs.r[15] + 1)
void SuperFX::disassemble_alt0(char *output) {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
case (0x01): sprintf(t, "nop"); break;
case (0x02): sprintf(t, "cache"); break;
case (0x03): sprintf(t, "lsr"); break;
case (0x04): sprintf(t, "rol"); break;
case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break;
case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break;
case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break;
case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break;
case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break;
case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break;
case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break;
case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break;
case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break;
case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break;
case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break;
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break;
case (0x3c): sprintf(t, "loop"); break;
case (0x3d): sprintf(t, "alt1"); break;
case (0x3e): sprintf(t, "alt2"); break;
case (0x3f): sprintf(t, "alt3"); break;
case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break;
case (0x4c): sprintf(t, "plot"); break;
case (0x4d): sprintf(t, "swap"); break;
case (0x4e): sprintf(t, "color"); break;
case (0x4f): sprintf(t, "not"); break;
case16(0x50): sprintf(t, "add r%u", op0 & 15); break;
case16(0x60): sprintf(t, "sub r%u", op0 & 15); break;
case (0x70): sprintf(t, "merge"); break;
case15(0x71): sprintf(t, "and r%u", op0 & 15); break;
case16(0x80): sprintf(t, "mult r%u", op0 & 15); break;
case (0x90): sprintf(t, "sbk"); break;
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
case (0x95): sprintf(t, "sex"); break;
case (0x96): sprintf(t, "asr"); break;
case (0x97): sprintf(t, "ror"); break;
case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break;
case (0x9e): sprintf(t, "lob"); break;
case (0x9f): sprintf(t, "fmult"); break;
case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib");
case15(0xc1): sprintf(t, "or r%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "getc"); break;
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
case (0xef): sprintf(t, "getb"); break;
case16(0xf0): sprintf(t, "iwt r%u,#$%.2x%.2x", op0 & 15, op2, op1); break;
}
strcat(output, t);
}
void SuperFX::disassemble_alt1(char *output) {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
case (0x01): sprintf(t, "nop"); break;
case (0x02): sprintf(t, "cache"); break;
case (0x03): sprintf(t, "lsr"); break;
case (0x04): sprintf(t, "rol"); break;
case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break;
case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break;
case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break;
case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break;
case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break;
case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break;
case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break;
case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break;
case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break;
case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break;
case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break;
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break;
case (0x3c): sprintf(t, "loop"); break;
case (0x3d): sprintf(t, "alt1"); break;
case (0x3e): sprintf(t, "alt2"); break;
case (0x3f): sprintf(t, "alt3"); break;
case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break;
case (0x4c): sprintf(t, "rpix"); break;
case (0x4d): sprintf(t, "swap"); break;
case (0x4e): sprintf(t, "cmode"); break;
case (0x4f): sprintf(t, "not"); break;
case16(0x50): sprintf(t, "adc r%u", op0 & 15); break;
case16(0x60): sprintf(t, "sbc r%u", op0 & 15); break;
case (0x70): sprintf(t, "merge"); break;
case15(0x71): sprintf(t, "bic r%u", op0 & 15); break;
case16(0x80): sprintf(t, "umult r%u", op0 & 15); break;
case (0x90): sprintf(t, "sbk"); break;
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
case (0x95): sprintf(t, "sex"); break;
case (0x96): sprintf(t, "div2"); break;
case (0x97): sprintf(t, "ror"); break;
case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break;
case (0x9e): sprintf(t, "lob"); break;
case (0x9f): sprintf(t, "lmult"); break;
case16(0xa0): sprintf(t, "lms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib"); break;
case15(0xc1): sprintf(t, "xor r%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "getc"); break;
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
case (0xef): sprintf(t, "getbh"); break;
case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break;
}
strcat(output, t);
}
void SuperFX::disassemble_alt2(char *output) {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
case (0x01): sprintf(t, "nop"); break;
case (0x02): sprintf(t, "cache"); break;
case (0x03): sprintf(t, "lsr"); break;
case (0x04): sprintf(t, "rol"); break;
case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break;
case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break;
case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break;
case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break;
case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break;
case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break;
case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break;
case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break;
case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break;
case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break;
case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break;
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break;
case (0x3c): sprintf(t, "loop"); break;
case (0x3d): sprintf(t, "alt1"); break;
case (0x3e): sprintf(t, "alt2"); break;
case (0x3f): sprintf(t, "alt3"); break;
case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break;
case (0x4c): sprintf(t, "plot"); break;
case (0x4d): sprintf(t, "swap"); break;
case (0x4e): sprintf(t, "color"); break;
case (0x4f): sprintf(t, "not"); break;
case16(0x50): sprintf(t, "add #%u", op0 & 15); break;
case16(0x60): sprintf(t, "sub #%u", op0 & 15); break;
case (0x70): sprintf(t, "merge"); break;
case15(0x71): sprintf(t, "and #%u", op0 & 15); break;
case16(0x80): sprintf(t, "mult #%u", op0 & 15); break;
case (0x90): sprintf(t, "sbk"); break;
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
case (0x95): sprintf(t, "sex"); break;
case (0x96): sprintf(t, "asr"); break;
case (0x97): sprintf(t, "ror"); break;
case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break;
case (0x9e): sprintf(t, "lob"); break;
case (0x9f): sprintf(t, "fmult"); break;
case16(0xa0): sprintf(t, "sms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib"); break;
case15(0xc1): sprintf(t, "or #%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "ramb"); break;
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
case (0xef): sprintf(t, "getbl"); break;
case16(0xf0): sprintf(t, "sm r%u", op0 & 15); break;
}
strcat(output, t);
}
void SuperFX::disassemble_alt3(char *output) {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
case (0x01): sprintf(t, "nop"); break;
case (0x02): sprintf(t, "cache"); break;
case (0x03): sprintf(t, "lsr"); break;
case (0x04): sprintf(t, "rol"); break;
case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break;
case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break;
case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break;
case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break;
case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break;
case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break;
case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break;
case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break;
case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break;
case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break;
case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break;
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break;
case (0x3c): sprintf(t, "loop"); break;
case (0x3d): sprintf(t, "alt1"); break;
case (0x3e): sprintf(t, "alt2"); break;
case (0x3f): sprintf(t, "alt3"); break;
case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break;
case (0x4c): sprintf(t, "rpix"); break;
case (0x4d): sprintf(t, "swap"); break;
case (0x4e): sprintf(t, "cmode"); break;
case (0x4f): sprintf(t, "not"); break;
case16(0x50): sprintf(t, "adc #%u", op0 & 15); break;
case16(0x60): sprintf(t, "cmp r%u", op0 & 15); break;
case (0x70): sprintf(t, "merge"); break;
case15(0x71): sprintf(t, "bic #%u", op0 & 15); break;
case16(0x80): sprintf(t, "umult #%u", op0 & 15); break;
case (0x90): sprintf(t, "sbk"); break;
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
case (0x95): sprintf(t, "sex"); break;
case (0x96): sprintf(t, "div2"); break;
case (0x97): sprintf(t, "ror"); break;
case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break;
case (0x9e): sprintf(t, "lob"); break;
case (0x9f): sprintf(t, "lmult"); break;
case16(0xa0): sprintf(t, "lms r%u", op0 & 15); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib"); break;
case15(0xc1): sprintf(t, "xor #%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "romb"); break;
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
case (0xef): sprintf(t, "getbs"); break;
case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break;
}
strcat(output, t);
}
#undef case4
#undef case6
#undef case12
#undef case15
#undef case16
#undef op0
#undef op1
#undef op2
#endif

View File

@@ -0,0 +1,5 @@
void disassemble_opcode(char *output);
void disassemble_alt0(char *output);
void disassemble_alt1(char *output);
void disassemble_alt2(char *output);
void disassemble_alt3(char *output);

View File

@@ -0,0 +1,71 @@
#ifdef SUPERFX_CPP
uint8 SuperFX::op_read(uint16 addr) {
uint16 offset = addr - regs.cbr;
if(offset < 512) {
if(cache.valid[offset >> 4] == false) {
unsigned dp = offset & 0xfff0;
unsigned sp = (regs.pbr << 16) + ((regs.cbr + dp) & 0xfff0);
for(unsigned n = 0; n < 16; n++) {
add_clocks(memory_access_speed);
cache.buffer[dp++] = superfxbus.read(sp++);
}
cache.valid[offset >> 4] = true;
} else {
add_clocks(cache_access_speed);
}
return cache.buffer[offset];
}
if(regs.pbr <= 0x5f) {
//$[00-5f]:[0000-ffff] ROM
rombuffer_sync();
add_clocks(memory_access_speed);
return superfxbus.read((regs.pbr << 16) + addr);
} else {
//$[60-7f]:[0000-ffff] RAM
rambuffer_sync();
add_clocks(memory_access_speed);
return superfxbus.read((regs.pbr << 16) + addr);
}
}
uint8 SuperFX::peekpipe() {
uint8 result = regs.pipeline;
regs.pipeline = op_read(regs.r[15]);
r15_modified = false;
return result;
}
uint8 SuperFX::pipe() {
uint8 result = regs.pipeline;
regs.pipeline = op_read(++regs.r[15]);
r15_modified = false;
return result;
}
void SuperFX::cache_flush() {
for(unsigned n = 0; n < 32; n++) cache.valid[n] = false;
}
uint8 SuperFX::cache_mmio_read(uint16 addr) {
addr = (addr + regs.cbr) & 511;
return cache.buffer[addr];
}
void SuperFX::cache_mmio_write(uint16 addr, uint8 data) {
addr = (addr + regs.cbr) & 511;
cache.buffer[addr] = data;
if((addr & 15) == 15) cache.valid[addr >> 4] = true;
}
void SuperFX::memory_reset() {
for(unsigned n = 0; n < 512; n++) cache.buffer[n] = 0x00;
for(unsigned n = 0; n < 32; n++) cache.valid[n] = false;
for(unsigned n = 0; n < 2; n++) {
pixelcache[n].offset = ~0;
pixelcache[n].bitpend = 0x00;
}
}
#endif

View File

@@ -0,0 +1,9 @@
uint8 op_read(uint16 addr);
alwaysinline uint8 peekpipe();
alwaysinline uint8 pipe();
void cache_flush();
uint8 cache_mmio_read(uint16 addr);
void cache_mmio_write(uint16 addr, uint8 data);
void memory_reset();

View File

@@ -0,0 +1,118 @@
#ifdef SUPERFX_CPP
uint8 SuperFX::mmio_read(unsigned addr) {
scheduler.sync_cpucop();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {
return cache_mmio_read(addr - 0x3100);
}
if(addr >= 0x3000 && addr <= 0x301f) {
return regs.r[(addr >> 1) & 15] >> ((addr & 1) << 3);
}
switch(addr) {
case 0x3030: {
return regs.sfr >> 0;
}
case 0x3031: {
uint8 r = regs.sfr >> 8;
regs.sfr.irq = 0;
cpu.regs.irq = 0;
return r;
}
case 0x3034: {
return regs.pbr;
}
case 0x3036: {
return regs.rombr;
}
case 0x303b: {
return regs.vcr;
}
case 0x303c: {
return regs.rambr;
}
case 0x303e: {
return regs.cbr >> 0;
}
case 0x303f: {
return regs.cbr >> 8;
}
}
return 0x00;
}
void SuperFX::mmio_write(unsigned addr, uint8 data) {
scheduler.sync_cpucop();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {
return cache_mmio_write(addr - 0x3100, data);
}
if(addr >= 0x3000 && addr <= 0x301f) {
unsigned n = (addr >> 1) & 15;
if((addr & 1) == 0) {
regs.r[n] = (regs.r[n] & 0xff00) | data;
} else {
regs.r[n] = (data << 8) | (regs.r[n] & 0xff);
}
if(addr == 0x301f) regs.sfr.g = 1;
return;
}
switch(addr) {
case 0x3030: {
bool g = regs.sfr.g;
regs.sfr = (regs.sfr & 0xff00) | (data << 0);
if(g == 1 && regs.sfr.g == 0) {
regs.cbr = 0x0000;
cache_flush();
}
} break;
case 0x3031: {
regs.sfr = (data << 8) | (regs.sfr & 0x00ff);
} break;
case 0x3033: {
regs.bramr = data;
} break;
case 0x3034: {
regs.pbr = data & 0x7f;
cache_flush();
} break;
case 0x3037: {
regs.cfgr = data;
update_speed();
} break;
case 0x3038: {
regs.scbr = data;
} break;
case 0x3039: {
regs.clsr = data;
update_speed();
} break;
case 0x303a: {
regs.scmr = data;
} break;
}
}
#endif

View File

@@ -0,0 +1,2 @@
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);

View File

@@ -0,0 +1,94 @@
#ifdef SUPERFX_CPP
void SuperFX::serialize(serializer &s) {
//superfx.hpp
s.integer(clockmode);
s.integer(instruction_counter);
//core/registers.hpp
s.integer(regs.pipeline);
s.integer(regs.ramaddr);
s.integer(regs.r[ 0].data);
s.integer(regs.r[ 1].data);
s.integer(regs.r[ 2].data);
s.integer(regs.r[ 3].data);
s.integer(regs.r[ 4].data);
s.integer(regs.r[ 5].data);
s.integer(regs.r[ 6].data);
s.integer(regs.r[ 7].data);
s.integer(regs.r[ 8].data);
s.integer(regs.r[ 9].data);
s.integer(regs.r[10].data);
s.integer(regs.r[11].data);
s.integer(regs.r[12].data);
s.integer(regs.r[13].data);
s.integer(regs.r[14].data);
s.integer(regs.r[15].data);
s.integer(regs.sfr.irq);
s.integer(regs.sfr.b);
s.integer(regs.sfr.ih);
s.integer(regs.sfr.il);
s.integer(regs.sfr.alt2);
s.integer(regs.sfr.alt1);
s.integer(regs.sfr.r);
s.integer(regs.sfr.g);
s.integer(regs.sfr.ov);
s.integer(regs.sfr.s);
s.integer(regs.sfr.cy);
s.integer(regs.sfr.z);
s.integer(regs.pbr);
s.integer(regs.rombr);
s.integer(regs.rambr);
s.integer(regs.cbr);
s.integer(regs.scbr);
s.integer(regs.scmr.ht);
s.integer(regs.scmr.ron);
s.integer(regs.scmr.ran);
s.integer(regs.scmr.md);
s.integer(regs.colr);
s.integer(regs.por.obj);
s.integer(regs.por.freezehigh);
s.integer(regs.por.highnibble);
s.integer(regs.por.dither);
s.integer(regs.por.transparent);
s.integer(regs.bramr);
s.integer(regs.vcr);
s.integer(regs.cfgr.irq);
s.integer(regs.cfgr.ms0);
s.integer(regs.clsr);
s.integer(regs.romcl);
s.integer(regs.romdr);
s.integer(regs.ramcl);
s.integer(regs.ramar);
s.integer(regs.ramdr);
s.integer(regs.sreg);
s.integer(regs.dreg);
s.array(cache.buffer);
s.array(cache.valid);
for(unsigned i = 0; i < 2; i++) {
s.integer(pixelcache[i].offset);
s.integer(pixelcache[i].bitpend);
s.array(pixelcache[i].data);
}
//timing/timing.hpp
s.integer(cache_access_speed);
s.integer(memory_access_speed);
s.integer(r15_modified);
}
#endif

View File

@@ -0,0 +1,79 @@
#include <../base.hpp>
#define SUPERFX_CPP
namespace SNES {
#include "serialization.cpp"
#include "bus/bus.cpp"
#include "core/core.cpp"
#include "memory/memory.cpp"
#include "mmio/mmio.cpp"
#include "timing/timing.cpp"
#include "disasm/disasm.cpp"
SuperFX superfx;
void SuperFX::enter() {
while(true) {
if(scheduler.sync == Scheduler::SyncAll) {
scheduler.exit(Scheduler::SynchronizeEvent);
}
if(regs.sfr.g == 0) {
add_clocks(6);
scheduler.sync_copcpu();
continue;
}
(this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])();
if(r15_modified == false) regs.r[15]++;
if(++instruction_counter >= 128) {
instruction_counter = 0;
scheduler.sync_copcpu();
}
}
}
void SuperFX::init() {
initialize_opcode_table();
regs.r[14].on_modify = bind(&SuperFX::r14_modify, this);
regs.r[15].on_modify = bind(&SuperFX::r15_modify, this);
}
void SuperFX::enable() {
for(unsigned i = 0x3000; i <= 0x32ff; i++) memory::mmio.map(i, *this);
}
void SuperFX::power() {
clockmode = config.superfx.speed;
reset();
}
void SuperFX::reset() {
superfxbus.init();
instruction_counter = 0;
for(unsigned n = 0; n < 16; n++) regs.r[n] = 0x0000;
regs.sfr = 0x0000;
regs.pbr = 0x00;
regs.rombr = 0x00;
regs.rambr = 0;
regs.cbr = 0x0000;
regs.scbr = 0x00;
regs.scmr = 0x00;
regs.colr = 0x00;
regs.por = 0x00;
regs.bramr = 0;
regs.vcr = 0x04;
regs.cfgr = 0x00;
regs.clsr = 0;
regs.pipeline = 0x01; //nop
regs.ramaddr = 0x0000;
regs.reset();
memory_reset();
timing_reset();
}
}

View File

@@ -0,0 +1,26 @@
#include "bus/bus.hpp"
class SuperFX : public MMIO {
public:
#include "core/core.hpp"
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
#include "timing/timing.hpp"
#include "disasm/disasm.hpp"
void enter();
void init();
void enable();
void power();
void reset();
void serialize(serializer&);
private:
unsigned clockmode;
unsigned instruction_counter;
};
extern SuperFX superfx;
extern SuperFXBus superfxbus;

View File

@@ -0,0 +1,97 @@
#ifdef SUPERFX_CPP
void SuperFX::add_clocks(unsigned clocks) {
if(regs.romcl) {
regs.romcl -= min(clocks, regs.romcl);
if(regs.romcl == 0) {
regs.sfr.r = 0;
regs.romdr = superfxbus.read((regs.rombr << 16) + regs.r[14]);
}
}
if(regs.ramcl) {
regs.ramcl -= min(clocks, regs.ramcl);
if(regs.ramcl == 0) {
superfxbus.write(0x700000 + (regs.rambr << 16) + regs.ramar, regs.ramdr);
}
}
scheduler.addclocks_cop(clocks);
scheduler.sync_copcpu();
}
void SuperFX::rombuffer_sync() {
if(regs.romcl) add_clocks(regs.romcl);
}
void SuperFX::rombuffer_update() {
regs.sfr.r = 1;
regs.romcl = memory_access_speed;
}
uint8 SuperFX::rombuffer_read() {
rombuffer_sync();
return regs.romdr;
}
void SuperFX::rambuffer_sync() {
if(regs.ramcl) add_clocks(regs.ramcl);
}
uint8 SuperFX::rambuffer_read(uint16 addr) {
rambuffer_sync();
return superfxbus.read(0x700000 + (regs.rambr << 16) + addr);
}
void SuperFX::rambuffer_write(uint16 addr, uint8 data) {
rambuffer_sync();
regs.ramcl = memory_access_speed;
regs.ramar = addr;
regs.ramdr = data;
}
void SuperFX::r14_modify(uint16 data) {
regs.r[14].data = data;
rombuffer_update();
}
void SuperFX::r15_modify(uint16 data) {
regs.r[15].data = data;
r15_modified = true;
}
void SuperFX::update_speed() {
//force SuperFX1 mode?
if(clockmode == 1) {
cache_access_speed = 2;
memory_access_speed = 6;
return;
}
//force SuperFX2 mode?
if(clockmode == 2) {
cache_access_speed = 1;
memory_access_speed = 5;
regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode
return;
}
//default: allow S-CPU to select mode
cache_access_speed = (regs.clsr ? 1 : 2);
memory_access_speed = (regs.clsr ? 5 : 6);
if(regs.clsr) regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode
}
void SuperFX::timing_reset() {
update_speed();
r15_modified = false;
regs.romcl = 0;
regs.romdr = 0;
regs.ramcl = 0;
regs.ramar = 0;
regs.ramdr = 0;
}
#endif

View File

@@ -0,0 +1,19 @@
unsigned cache_access_speed;
unsigned memory_access_speed;
bool r15_modified;
void add_clocks(unsigned clocks);
void rombuffer_sync();
void rombuffer_update();
uint8 rombuffer_read();
void rambuffer_sync();
uint8 rambuffer_read(uint16 addr);
void rambuffer_write(uint16 addr, uint8 data);
void r14_modify(uint16);
void r15_modify(uint16);
void update_speed();
void timing_reset();

View File

@@ -0,0 +1,140 @@
#include <../base.hpp>
#define SUPERGAMEBOY_CPP
namespace SNES {
SuperGameBoy supergameboy;
void SuperGameBoy::enter() {
scheduler.clock.cop_freq = (version == SuperGameBoy1 ? 2147727 : 2097152);
if(!sgb_run) while(true) {
if(scheduler.sync == Scheduler::SyncAll) {
scheduler.exit(Scheduler::SynchronizeEvent);
}
audio.coprocessor_sample(0, 0);
scheduler.addclocks_cop(1);
scheduler.sync_copcpu();
}
while(true) {
if(scheduler.sync == Scheduler::SyncAll) {
scheduler.exit(Scheduler::SynchronizeEvent);
}
unsigned samples = sgb_run(samplebuffer, 16);
for(unsigned i = 0; i < samples; i++) {
int16 left = samplebuffer[i] >> 0;
int16 right = samplebuffer[i] >> 16;
//SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects
audio.coprocessor_sample(left / 3, right / 3);
}
scheduler.addclocks_cop(samples);
scheduler.sync_copcpu();
}
}
uint8 SuperGameBoy::mmio_read(unsigned addr) {
addr &= 0xffff;
if(addr == 0x2181) return mmio[0]->mmio_read(addr);
if(addr == 0x2182) return mmio[1]->mmio_read(addr);
if(addr == 0x420b) return mmio[2]->mmio_read(addr);
return 0x00;
}
void SuperGameBoy::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if(addr == 0x2181) {
row = (row & 0xff00) | (data << 0);
mmio[0]->mmio_write(addr, data);
}
if(addr == 0x2182) {
row = (row & 0x00ff) | (data << 8);
mmio[1]->mmio_write(addr, data);
}
if(addr == 0x420b) {
if(data == 0x10 && sgb_row) {
if(row >= 0x5000 && row <= 0x6540) sgb_row((row - 0x5000) / 320);
if(row >= 0x6800 && row <= 0x7d40) sgb_row((row - 0x6800) / 320);
}
mmio[2]->mmio_write(addr, data);
}
}
uint8 SuperGameBoy::read(unsigned addr) {
if(sgb_read) return sgb_read(addr);
return 0x00;
}
void SuperGameBoy::write(unsigned addr, uint8 data) {
if(sgb_write) sgb_write(addr, data);
}
void SuperGameBoy::init() {
if(open("supergameboy")) {
sgb_rom = sym("sgb_rom");
sgb_ram = sym("sgb_ram");
sgb_rtc = sym("sgb_rtc");
sgb_init = sym("sgb_init");
sgb_term = sym("sgb_term");
sgb_power = sym("sgb_power");
sgb_reset = sym("sgb_reset");
sgb_row = sym("sgb_row");
sgb_read = sym("sgb_read");
sgb_write = sym("sgb_write");
sgb_run = sym("sgb_run");
sgb_save = sym("sgb_save");
sgb_serialize = sym("sgb_serialize");
}
}
void SuperGameBoy::enable() {
mmio[0] = memory::mmio.mmio[0x2181 - 0x2000];
mmio[1] = memory::mmio.mmio[0x2182 - 0x2000];
mmio[2] = memory::mmio.mmio[0x420b - 0x2000];
memory::mmio.map(0x2181, *this);
memory::mmio.map(0x2182, *this);
memory::mmio.map(0x420b, *this);
}
void SuperGameBoy::power() {
version = (cartridge.type() == Cartridge::TypeSuperGameBoy1Bios ? SuperGameBoy1 : SuperGameBoy2);
audio.coprocessor_enable(true);
audio.coprocessor_frequency(version == SuperGameBoy1 ? 2147727.0 : 2097152.0);
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
sgb_rom(memory::gbrom.data(), memory::gbrom.size() == -1U ? 0 : memory::gbrom.size());
sgb_ram(memory::gbram.data(), memory::gbram.size() == -1U ? 0 : memory::gbram.size());
sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size());
if(sgb_init) sgb_init(version);
if(sgb_power) sgb_power();
}
void SuperGameBoy::reset() {
if(sgb_reset) sgb_reset();
}
void SuperGameBoy::unload() {
if(sgb_term) sgb_term();
}
void SuperGameBoy::serialize(serializer &s) {
s.integer(row);
s.integer(version);
if(sgb_serialize) sgb_serialize(s);
}
}

View File

@@ -0,0 +1,41 @@
class SuperGameBoy : public MMIO, public Memory, public library {
public:
void enter();
MMIO *mmio[3];
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void init();
void enable();
void power();
void reset();
void unload();
void serialize(serializer&);
private:
uint32_t samplebuffer[4096];
unsigned row;
bool version;
enum { SuperGameBoy1 = 0, SuperGameBoy2 = 1 };
function<void (uint8_t*, unsigned)> sgb_rom;
function<void (uint8_t*, unsigned)> sgb_ram;
function<void (uint8_t*, unsigned)> sgb_rtc;
function<bool (bool)> sgb_init;
function<void ()> sgb_term;
function<void ()> sgb_power;
function<void ()> sgb_reset;
function<void (unsigned)> sgb_row;
function<uint8 (uint16)> sgb_read;
function<void (uint16, uint8)> sgb_write;
function<unsigned (uint32_t*, unsigned)> sgb_run;
function<void ()> sgb_save;
function<void (serializer&)> sgb_serialize;
};
extern SuperGameBoy supergameboy;

View File

@@ -1 +1 @@
@mingw32-make platform=win compiler=mingw32-gcc clean
@mingw32-make clean

View File

@@ -1 +0,0 @@
make platform=x compiler=gcc clean

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