PicoDrive (WIP)
This commit is contained in:
parent
bc9726f687
commit
47535f8158
|
@ -1117,6 +1117,8 @@
|
|||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGXControlConverter.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx64\LibGPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\PicoDrive\LibPicoDrive.cs" />
|
||||
<Compile Include="Consoles\Sega\PicoDrive\PicoDrive.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\FilePiping.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\LibSaturnus.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\Saturnus.cs" />
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
|
||||
{
|
||||
public abstract class LibPicoDrive : LibWaterboxCore
|
||||
{
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
|
||||
{
|
||||
[CoreAttributes("PicoDrive", "notaz", true, false,
|
||||
"0e352905c7aa80b166933970abbcecfce96ad64e", "https://github.com/notaz/picodrive", false)]
|
||||
public class PicoDrive : WaterboxCore
|
||||
{
|
||||
private LibPicoDrive _core;
|
||||
|
||||
[CoreConstructor("GEN")]
|
||||
public PicoDrive(CoreComm comm, byte[] rom)
|
||||
: base(comm, new Configuration
|
||||
{
|
||||
MaxSamples = 2048,
|
||||
DefaultWidth = 320,
|
||||
DefaultHeight = 224,
|
||||
MaxWidth = 320,
|
||||
MaxHeight = 480,
|
||||
SystemId = "GEN"
|
||||
})
|
||||
{
|
||||
_core = PreInit<LibPicoDrive>(new PeRunnerOptions
|
||||
{
|
||||
Filename = "picodrive.wbx",
|
||||
SbrkHeapSizeKB = 4096,
|
||||
SealedHeapSizeKB = 4096,
|
||||
InvisibleHeapSizeKB = 4096,
|
||||
MmapHeapSizeKB = 65536,
|
||||
PlainHeapSizeKB = 4096,
|
||||
});
|
||||
|
||||
_exe.AddReadonlyFile(rom, "romfile.md");
|
||||
if (!_core.Init())
|
||||
throw new InvalidOperationException("Core rejected the rom!");
|
||||
_exe.RemoveReadonlyFile("romfile.md");
|
||||
PostInit();
|
||||
}
|
||||
|
||||
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
return new LibWaterboxCore.FrameInfo();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": false,
|
||||
"editor.detectIndentation": false
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": false,
|
||||
"editor.detectIndentation": false
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
notaz
|
||||
core, 32X emulation, CD code, ARM asm renderers, dynamic recompilers,
|
||||
Pandora, GPH device, PSP, Gizmondo ports, CPU core hacks
|
||||
lots of additional coding (see changeLog).
|
||||
Homepage: http://notaz.gp2x.de/
|
||||
|
||||
fDave
|
||||
project starter
|
||||
Cyclone 68000 core and PicoDrive core itself
|
||||
|
||||
Chui
|
||||
FAME/C 68k interpreter core
|
||||
(based on C68K by Stephane Dallongeville)
|
||||
|
||||
Stephane Dallongeville (written), NJ (optimized)
|
||||
CZ80 Z80 interpreter core
|
||||
|
||||
Reesy & FluBBa
|
||||
DrZ80, the Z80 interpreter written in ARM assembly.
|
||||
Homepage: http://reesy.gp32x.de/ (defunct)
|
||||
|
||||
Tatsuyuki Satoh, Jarek Burczynski, MAME development
|
||||
software implementation of Yamaha FM sound generator
|
||||
|
||||
MAME development
|
||||
Texas Instruments SN76489 / SN76496 programmable tone/noise generator
|
||||
Homepage: http://www.mame.net/
|
||||
|
||||
Eke-Eke
|
||||
CD graphics processor and CD controller implementation (from Genesis Plus GX)
|
||||
|
||||
|
||||
Additional thanks
|
||||
-----------------
|
||||
|
||||
* Charles MacDonald (http://cgfm2.emuviews.com/) for old but still very useful
|
||||
info about genesis hardware.
|
||||
* Steve Snake for all that he has done for Genesis emulation scene.
|
||||
* Stephane Dallongeville for writing Gens and making it open source.
|
||||
* Tasco Deluxe for his reverse engineering work on SVP and some mappers.
|
||||
* Bart Trzynadlowski for his SSFII and 68000 docs.
|
||||
* Haze for his research (http://mamedev.emulab.it/haze/).
|
||||
* Lordus, Exophase and Rokas for various ideas.
|
||||
* Nemesis for his YM2612, VDP research and docs.
|
||||
* Eke-Eke for sharing the knowledge and his work on Genesis Plus GX.
|
||||
* Many posters at spritesmind.net forums for valuable information.
|
||||
* Mark and Jean-loup for zlib library.
|
||||
* ketchupgun for the skin.
|
||||
* GP2X specific help: rlyeh, Squidge, Dzz, A_SN, Alex and GP32X posters.
|
||||
* Gizmondo code: Kingcdr, Reesy, jens.l (for the device itself)
|
||||
* Hardware: craigix (GP2X), EvilDragon (Wiz, Caanoo, Pandora, ...)
|
||||
and jens.l (Gizmondo)
|
||||
* Paul Cercueil for OpenDingux port.
|
||||
* Inder for some graphics.
|
||||
* squarepusher for some libretro fixes
|
||||
* Anyone else I forgot. Let me know if it's you.
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
Redistribution and use of this code or any derivative works are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions may not be sold, nor may they be used in a commercial
|
||||
product or activity.
|
||||
|
||||
* Redistributions that are modified from the original source must include the
|
||||
complete source code, including the source code for all components used by a
|
||||
binary built from the modified sources. However, as a special exception, the
|
||||
source code distributed need not include anything that is normally distributed
|
||||
(in either source or binary form) with the major components (compiler, kernel,
|
||||
and so on) of the operating system on which the executable runs, unless that
|
||||
component itself accompanies the executable.
|
||||
|
||||
* Redistributions must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,487 @@
|
|||
1.91 (2013-10-12)
|
||||
+ Added OpenDingux support (Paul Cercueil).
|
||||
* Save directory changed to ~/.picodrive/ for generic platform build
|
||||
(Paul Cercueil).
|
||||
+ Revived GP2X/Caanoo/Wiz support.
|
||||
+ Switched to cleaner CD controller code from Eke-Eke's Genesis Plus GX.
|
||||
* Fixed overflow issue where cd emulation would break after
|
||||
~10 minutes of gameplay.
|
||||
* Fixed synchronization issue where model1 CD BIOS would randomly hang.
|
||||
|
||||
1.90 (2013-09-24)
|
||||
+ 32X+CD emulation has been implemented.
|
||||
+ CD graphics processor code has been replaced with much cleaner Eke-Eke's
|
||||
implementation from Genesis Plus GX.
|
||||
+ CD PCM code has been completely rewritten.
|
||||
* Various CD compatibility issues have been solved. Hopefully no more
|
||||
regressions this time.
|
||||
* pandora: fixed tv-out (again), added automatic layer switching
|
||||
* libretro: fixed crackling sound for some games, added some core options
|
||||
* sdl: multiple joystick support has been fixed (Victor Luchits)
|
||||
|
||||
1.85 (2013-08-31)
|
||||
* Lots of 32X compatibility and accuracy improvements. All commercial games
|
||||
are booting now, but some still have issues.
|
||||
* Fixed some regressions in MegaCD code, like hang in jap BIOS.
|
||||
* Implemented pause for SMS.
|
||||
* Updated UI with improvements from PCSX ReARMed.
|
||||
* Frontend timing has been rewritten, should no longer slowly desync from
|
||||
LCD on pandora.
|
||||
* Added libretro and SDL 32/64bit ports, fixed compatibility issues with
|
||||
Android, iOS.
|
||||
* Various other things I forgot (it has been a while since last release..)
|
||||
|
||||
1.80 (2010-09-19)
|
||||
+ Added Caanoo support. Now the GP2X binary supports GP2X F100/F200, Wiz
|
||||
and Caanoo. Lots of internal refactoring to support this.
|
||||
+ Enabled 32X and SMS code. It's still unfinished but better release something
|
||||
now than wait even more (it has been in development for more then a year now
|
||||
due to various other projects or simply lack of time).
|
||||
+ Pandora: added hardware scaler support, including ability to resize the
|
||||
layer and control filtering.
|
||||
+ GP2X: Added basic line-doubling vertical scaling option.
|
||||
* Changed the way keys are bound, no need to unbind old one any more.
|
||||
* Handle MP3s with ID3 tags better (some MP3s with ID3 did not play).
|
||||
* Improved shadow/hilight color levels.
|
||||
* Fixed broken cheat support.
|
||||
|
||||
1.80beta2
|
||||
* Pandora: updated documentation.
|
||||
|
||||
1.80beta1 (2010-06-02)
|
||||
+ Added pandora port.
|
||||
* Internal refactoring for 32x/SMS support.
|
||||
* Move mapper database to external file.
|
||||
+ Added preliminary SMS emulation.
|
||||
+ Added emulation of 32x peripherals including VDP. More work is needed here.
|
||||
+ ARM: Added new SH2 recompiler for 32x. Some unification with SVP one.
|
||||
- Disabled most of the above bacause I'm not yet happy with the results.
|
||||
|
||||
1.56 (2009-09-19)
|
||||
* Changed sync in Sega CD emulation again. Should fix games that
|
||||
broke after changes in 1.51a.
|
||||
* Fixed default keys rebinding when they shouldn't.
|
||||
* Fixed sram being loaded from wrong game.
|
||||
* Emu should no longer hang shortly after using fast-forward.
|
||||
* Fixed save states sometimes no longer showing up in save state menu.
|
||||
* ARM: some asm code refactoring for slight speed improvement.
|
||||
|
||||
1.55
|
||||
+ Added Wiz support. Now the same GP2X binary supports F100/F200 and Wiz.
|
||||
* Changed shadow/hilight handling a bit, fixes some effects in Pirates! Gold.
|
||||
* Complete input code rewrite. This fixes some limitations like not allowing
|
||||
to control both players using single input device. It also allows to use
|
||||
more devices (like keyboards) on Linux based devices.
|
||||
* Options menu has been reordered, "restore defaults" option added.
|
||||
|
||||
1.51b
|
||||
* Fixed a crash when uncompressed savestate is loaded.
|
||||
* Fixed an idle loop detection related hanging problem.
|
||||
* PSP: fixed another palette related regression.
|
||||
* UIQ3: updated frontend for the latest emu core.
|
||||
|
||||
1.51a
|
||||
* Fixed a sync problem between main and sub 68k. Should fix the hanging
|
||||
problem for some games.
|
||||
* ARM: fixed a crash when CD savestate is loaded just after loading ROM.
|
||||
|
||||
1.51
|
||||
* Improved bin_to_cso_mp3 tool, it should no longer complain about
|
||||
missing lame.exe even if it's in working dir.
|
||||
* Fixed a regression from 1.50, which caused slowdowns in Final Fight.
|
||||
* Fixed some regressions from 1.50 related to sprite limit and palette
|
||||
handling (caused graphical glitches in some games).
|
||||
+ Added ABC turbo actions to key config.
|
||||
* Some other minor adjustments.
|
||||
|
||||
1.50
|
||||
+ Added some basic support for Sega Pico, a MegaDrive-based toy.
|
||||
+ Added proper support for cue/bin images, including cdda playback.
|
||||
.cue sheets with iso/cso/mp3/wav files listed in them are now
|
||||
supported too (but 44kHz restriction still applies).
|
||||
+ Added bin_to_cso_mp3 tool, based on Exophase's bin_to_iso_ogg.
|
||||
The tool can convert .cue/.bin Sega CD images to .cso/.mp3.
|
||||
* Greatly improved Sega CD load times.
|
||||
* Changed how scheduling between 68k and z80 is handled. Improves
|
||||
performance for some games. Credits to Lordus for the idea.
|
||||
* YM2612 state was not 100% saved, this should be better now.
|
||||
* Improved renderer performance for shadow/hilight mode.
|
||||
* Added a hack for YM2612 frequency overflow issue (bleep noises
|
||||
in Shaq Fu, Spider-Man - The Animated Series (intro music), etc.)
|
||||
Credits to Nemesis @ spritesmind forum. Works only if sound rate
|
||||
is set to 44kHz.
|
||||
+ Implemented some sprite rendering improvements, as suggested by
|
||||
Exophase. Games with lots of sprites now perform better.
|
||||
+ Added better idle loop detection, based on Lordus' idea again.
|
||||
- "accurate timing" option removed, as disabling it no longer
|
||||
improves performance.
|
||||
- "accurate sprites" was removed too, the new sprite code can
|
||||
properly handle sprite priorities in all cases.
|
||||
* Timers adjusted again.
|
||||
* Improved .smd detection code.
|
||||
* ARM: fixed a bug in DrZ80 core, which could cause problems in
|
||||
some rare cases.
|
||||
* ARM: fixed a problem of occasional clicks on MP3 music start.
|
||||
* Minor general optimizations and menu improvements.
|
||||
* Fixed a bug in Sega CD savestate loader, where the game would
|
||||
sometimes crash after load.
|
||||
* Fixed a crash of games using eeprom (introduced in 1.40b).
|
||||
* PSP: fixed suspend/resume (hopefully for real).
|
||||
|
||||
1.40c
|
||||
* Fixed a problem with sound in Marble Madness.
|
||||
* GP2X: Fixed minor problem with key config.
|
||||
|
||||
1.40b
|
||||
* Fixed sprite masking code. Thanks to Lordus for explaining how it works.
|
||||
+ Added "disable sprite limit" option.
|
||||
+ PSP: added black level adjustment to display options.
|
||||
* Changed reset to act as 'soft' reset.
|
||||
+ Added detection for Puggsy (it doesn't really have sram).
|
||||
* Some small timing adjustments.
|
||||
|
||||
1.40a
|
||||
* GP2X: Fixed a binding problem with up and down keys.
|
||||
* Default game config no longer overrides global user config.
|
||||
|
||||
1.40
|
||||
+ Added support for SVP (Sega Virtua Processor) to emulate Virtua Racing,
|
||||
wrote ARM recompiler and some HLE code for VR. Credits to Exophase and
|
||||
Rokas for various ideas.
|
||||
* Changed config file format, files are now human-readable. Game specific
|
||||
configs are now held in single file (but old game config files are still
|
||||
read when new one is missing).
|
||||
* Fixed a bug where some key combos didn't work as expected.
|
||||
* Fixed a regression in renderer (ARM ports only, some graphic glitches in
|
||||
rare cases).
|
||||
* Adjusted fast renderer to work with more games, including VR.
|
||||
* Fixed a problem where SegaCD RAM cart data was getting lost on reset.
|
||||
* GP2X: Greatly reduced SegaCD FMV game slowdowns by disabling read-ahead
|
||||
in the Linux kernel and C library (thanks to Rokas and Exophase for ideas
|
||||
again). Be sure to keep "ReadAhead buffer" OFF to avoid slowdowns.
|
||||
+ PicoDrive now comes with a game config file for some games which need
|
||||
special settings, so they should now work out-of-the-box. More games will
|
||||
be added with later updates.
|
||||
+ GP2X: Files now can be deleted by pressing A+SELECT in the file browser.
|
||||
|
||||
1.35b
|
||||
* PSP: mp3 code should no longer fail on 1.5 firmware.
|
||||
+ PSP: added gamma adjustment option.
|
||||
+ Added .cso ISO format support. Useful for non-FMV games.
|
||||
* It is now possile to force a region after the ROM is loaded.
|
||||
* Fixed a sram bug in memhandlers (fixes Shining in the Darkness saves).
|
||||
* PSP: fixed another bug in memhanlers, which crashed the emu for some games
|
||||
(like NBA Jam and NHL 9x).
|
||||
+ PSP: added suspend/resume handling for Sega CD games.
|
||||
+ GP2X: added additional low volume levels for my late-night gaming sessions
|
||||
(in stereo mode only).
|
||||
+ GP2X: added "fast forward" action in key config. Not recommended to use for
|
||||
Sega CD, may case problems there.
|
||||
* Some other small tweaks I forgot about.
|
||||
|
||||
1.35a
|
||||
* PSP: fixed a bug which prevented to load any ROMs after testing the BIOS.
|
||||
* PSP: fixed incorrect CZ80 memory map setup, which caused Z80 crashes and
|
||||
graphics corruption in EU Mega CD model1 BIOS menus.
|
||||
+ PSP: added additional "set to 4:3 scaled" display option for convenience.
|
||||
+ PSP: Added an option to disable frame limitter (works only with non-auto frameskip).
|
||||
|
||||
1.35
|
||||
+ PSP port added. Lots of new code for it. Integrated modified FAME/C, CZ80 cores.
|
||||
+ Some minor generic optimizations.
|
||||
* Patched some code which was crashing under PSP, but was working in GP2X/Giz
|
||||
(although it should have crashed there too).
|
||||
* Readme updated.
|
||||
|
||||
1.34
|
||||
+ Gizmondo port added.
|
||||
+ Some new optimizations in memory handlers, and for shadow/hilight mode.
|
||||
+ Added some hacks to make more games work without enabling "accurate timing".
|
||||
* Adjusted timing for "accurate timing" mode and added preliminary VDP FIFO
|
||||
emulation. Fixes Double Dragon 2, tearing in Chaos Engine and some other games.
|
||||
* Fixed a few games not having sound at startup.
|
||||
* Updated serial EEPROM code to support more games. Thanks to EkeEke for
|
||||
providing info about additional EEPROM types and game mappers.
|
||||
* The above change fixed hang of NBA Jam.
|
||||
* Minor adjustments to control configurator.
|
||||
|
||||
1.33
|
||||
* Updated Cyclone core to 0.0088.
|
||||
+ Added A r k's usbjoy fix.
|
||||
+ Added "perfect vsync" option, which adjusts GP2X LCD refresh rate and syncs
|
||||
emulation to it to eliminate tearing and ensure smoothest scrolling possible.
|
||||
+ Added an option to use A_SN's gamma curve for gamma correction (improves dark
|
||||
and bright color display for mk2s).
|
||||
* Sometimes stray sounds were played after loading a savestate. Fixed.
|
||||
* Fixed a problem where >6MB mp3s were corrupted in memory (sound glitches in
|
||||
Snatcher).
|
||||
* PD no longer overwrites video player code in memory, video player now can be
|
||||
used after exiting PicoDrive.
|
||||
* Fixed a bug which was causing Sonic 3 code to deadlock in some rare conditions
|
||||
if "accurate timing" was not enabled.
|
||||
* Fixed support for large hacked ROMs like "Ultimate Mortal Kombat Trilogy".
|
||||
Upto 10MB hacked ROMs are supported now.
|
||||
+ Config profiles added (press left/right when saving config).
|
||||
* Changed key configuration behavior to the one from gpfce (should be more
|
||||
intuitive).
|
||||
+ Added some skinning capabilities to the menu system with default skin by
|
||||
ketchupgun. Delete skin directory if you want old behaviour.
|
||||
* Some other little tweaks I forgot about.
|
||||
|
||||
1.32
|
||||
+ Added some new scaling options.
|
||||
+ Added ability to reload CD images while game is running (needed for games
|
||||
with multiple CDs, like Night Trap).
|
||||
+ Added RAM cart emulation.
|
||||
* Fixed DMA timing emulation (caused lock-ups for some genesis games).
|
||||
* Idle loop detection was picking up wrong code and causing glitches, fixed.
|
||||
* The ym2612 code on 940 now can handle multiple updates per frame
|
||||
(fixes Thunger Force III "seiren" level drums for example).
|
||||
* Memory handlers were ignoring some writes to PSG chip, fixed (missing sounds in
|
||||
Popful Mail, Silpheed).
|
||||
* Improved z80 timing, should fix some sound problems.
|
||||
* Fixed a bug with sram register (fixes Phantasy Star 4).
|
||||
* ROM loader was incorrectly identifying some ROMs as invalid. Fixed.
|
||||
* Added code for PRG ram write protection register (Dungeon Explorer).
|
||||
* The memory mode register change in 1.31 was unsafe and caused some glitches in
|
||||
AH-3 Thunderstrike. Fixed.
|
||||
* Fixed a file descriptor leak.
|
||||
* Updated documentation, added Gmenu2x manual.
|
||||
|
||||
1.31
|
||||
* Changed the way memory mode register is read (fixes Lunar 2, broken in 1.30).
|
||||
* Fixed TAS opcode on sub-68k side (fixes Batman games).
|
||||
* File browser now filters out mp3s, saves and some other files, which are not ROMS.
|
||||
|
||||
1.30
|
||||
+ ISO files now can be zipped. Note that this causes VERY long loading times.
|
||||
+ Added data pre-buffering support, this allows to reduce frequency of short pauses
|
||||
in FMV games (caused by SD access), but makes those pauses longer.
|
||||
* Fixed PCM DMA transfers (intro FMV in Popful Mail).
|
||||
+ Properly implemented "decode" data transformation (Jaguar XJ220).
|
||||
* Integrated "better sync" code into cyclone code, what made this mode much faster.
|
||||
* Fixed a bug related to game specific config saving.
|
||||
* Frameskipper was skipping sound processing, what caused some audio desyncs. Fixed.
|
||||
* Fixed reset not working for some games.
|
||||
+ New assembly optimized memory handlers for CD (gives at least a few fps).
|
||||
Also re-enabled all optimizations from 0.964 release.
|
||||
+ New idle-loop detection code for sub-68k. Speeds up at least a few games.
|
||||
|
||||
1.201
|
||||
+ Added basic cheat support (GameGenie and Genecyst patches).
|
||||
|
||||
1.20
|
||||
* Fixed a long-standing problem in audio mixing code which caused slight distortions
|
||||
at lower sample rates.
|
||||
* Changed the way 920 and 940 communicates (again), should be more reliable and give
|
||||
slight performance increase.
|
||||
* Some optimizations in audio mixing code.
|
||||
* Some menu changes (background added, smaller font in ROM browser, savestate loader
|
||||
now can select slots).
|
||||
+ 1M mode DMA transfers implemented (used by FMV games like Night Trap and Sewer Shark).
|
||||
+ Games now can run code from WORD RAM in 1M mode (fixes Adventures of Willy Beamish).
|
||||
+ "Cell arrange" address mapping is now emulated (Heart of the alien).
|
||||
+ "Color numeric operation" is now emulated (text in Lunar 2, Silpheed intro graphics).
|
||||
+ "Better sync" option added (prevents some games from hanging).
|
||||
|
||||
1.14
|
||||
+ Region autodetection now can be customized.
|
||||
* When CDDA music tracks changed, old buffer contents were incorrectly played. Fixed.
|
||||
* BRAM is now automatically formatted (no need to enter BIOS menu and format any more).
|
||||
* Games now can be reset, CDDA music no longer breaks after loading another ISO.
|
||||
* Fixed a race condition between 920 and 940 which sometimes caused CDDA music not to play.
|
||||
+ Savestates implemented for Sega/Mega CD.
|
||||
+ PCM sound added.
|
||||
* Some mixer code rewritten in asm. 22kHz and 11kHz sound rates are now supported in
|
||||
Mega CD mode (but mp3s must still be 44kHz stereo).
|
||||
+ Timer emulation added.
|
||||
* CDC DMA tansfers fixed. Snatcher and probably some more games now boot.
|
||||
* 2M word RAM -> VDP transfers fixed, no more corruption in Ecco and some other games.
|
||||
|
||||
1.10
|
||||
+ GP2X: Added experimental Sega CD support.
|
||||
+ GP2X: Added partial gmv movie playback support.
|
||||
|
||||
0.964 (2006-12-03)
|
||||
* GP2X: Fixed a sound buffer underflow issue on lower sample rate modes, which was
|
||||
happening for NTSC games and causing sound clicks.
|
||||
* GP2X: Redone key config to better support USB joysticks (now multiple joysticks
|
||||
should be useable and configurable).
|
||||
+ GP2X: Added save confirmation option.
|
||||
+ GP2X: Added 940 CPU crash detection.
|
||||
+ ALL: UIQ3 port added.
|
||||
|
||||
0.963
|
||||
* GP2X: Gamma-reset-on-entering-menu bug fixed.
|
||||
* GP2X: Recompiled PicoDrive with gcc profiling option set as described here:
|
||||
http://www.gp32x.com/board/index.php?showtopic=28490
|
||||
|
||||
0.962
|
||||
* GP2X: Fixed an issue with incorrect sounds in some games when dualcore operation
|
||||
was enabled (for example punch sound in SOR).
|
||||
* GP2X: Limited max volume to 90, because higher values often cause distortions.
|
||||
* GP2X: Fixed a bug with lower res scaling.
|
||||
* GP2X: Gamma is now reset on exit.
|
||||
|
||||
0.96
|
||||
* ALL: Severely optimized MAME's YM2612 core, part of it is now rewritten in asm.
|
||||
+ GP2X: The YM2612's code now can be run in GP2X's ARM940T CPU, what causes large
|
||||
performance increase.
|
||||
* ALL: Accurate renderers are slightly faster now.
|
||||
+ GP2X: Using quadruple buffering instead of doublebuffer now, also updated
|
||||
framelimitter, this should eliminate some scrolling and tearing problems.
|
||||
* GP2X: Fixed some flickering issues of 8bit accurate renderer.
|
||||
+ GP2X: craigix's RAM timings now can be enabled in the menu (see advanced options).
|
||||
+ GP2X: Added ability to save config for specific games only.
|
||||
+ GP2X: Gamma control added (using GP2X's hardware capabilities for this).
|
||||
* GP2X: Volume keys are now configurable.
|
||||
+ GP2X: GnoStiC added USB joystick support, I made it possible to use it for
|
||||
player 2 control (currently untested).
|
||||
* GP2X: squidgehack is now applied through kernel module (cleaner way).
|
||||
|
||||
0.95
|
||||
* ALL: Fixed a bug in sprite renderer which was causing slowdowns for some games.
|
||||
+ GP2X: Added command line support
|
||||
+ GP2X: Added optional hardware scaling for lower-res games like Shining Force.
|
||||
* ALL: Sound chips are now sampled 2 times per frame. This fixed some games which
|
||||
had missing sounds (Vectorman 2 1st level, Thunder Force 3 water level,
|
||||
etc.).
|
||||
+ ALL: Added another accurate 8-bit renderer which is slightly faster and made it
|
||||
default.
|
||||
|
||||
0.945
|
||||
+ GP2X: Added frame limiter for frameskipped modes.
|
||||
* GP2X: Increased brightness a bit (unused pixel bits now also contain data).
|
||||
* GP2X: Suidgehack was not applied correctly (was applied before allocating some
|
||||
high memory and had no effect).
|
||||
|
||||
0.94
|
||||
+ Added GP2X port.
|
||||
* Improved interrupt timing, Mazin Saga and Burning Force now works.
|
||||
* Rewritten renderer code to better suit GP2X, should be faster on other
|
||||
ports too.
|
||||
+ Added support for banking used by 12-in-1 and 4-in-1 ROMs (thanks Haze).
|
||||
+ Added some protection device faking, used by some unlicensed games like
|
||||
Super Bubble Bobble, King of Fighters, Elf Wor, ... (thanks to Haze again)
|
||||
+ Added primitive Virtua Racing SVP faking, so menus can be seen now.
|
||||
|
||||
0.93
|
||||
* Fixed a problem with P900/P910 key configuration in FC mode.
|
||||
* Improved shadow/hilight mode emulation. Still not perfect, but should be
|
||||
enough for most games.
|
||||
+ Save state slots added.
|
||||
+ Region selector added.
|
||||
|
||||
0.92
|
||||
VDP changes:
|
||||
* VDP emulation is now more accurate (fixes flickering in Chase HQ II,
|
||||
Super Hang-On and some other problems in other games).
|
||||
* HV counter emulation is now much more accurate. Fixes the Asterix games,
|
||||
line in Road Rash 3, etc.
|
||||
* Minor sprite and layer scroll masking bugs fixed.
|
||||
+ Added partial interlace mode renderer (Sonic 2 vs mode)
|
||||
* Fixed a crash in both renderers when certain size window layers were used.
|
||||
+ Added emulation of shadow/hilight operator sprites. Other shadow/hilight
|
||||
effects are still unemulated.
|
||||
+ Sprite emulation is more accurate, sprite limit is emulated.
|
||||
+ Added "accurate sprites" option, which always draws sprites in correct
|
||||
order and emulates sprite collision bit, but is significantly slower.
|
||||
|
||||
Emulation changes:
|
||||
* Improved interrupt handling, added deferred interrupt emulation
|
||||
(Lemmings, etc).
|
||||
+ Added serial EEPROM SRAM support (Wonder Boy in Monster World,
|
||||
Megaman - The Wily Wars and many EA sports games like NBA Jam).
|
||||
+ Implemented ROM banking for Super Street Fighter II - The New Challengers
|
||||
* Updated to the latest version of DrZ80 core, integrated memory handlers
|
||||
in it for better performance. A noticeable performance increase, but save
|
||||
states may not work from the previous version (you can only use them with
|
||||
sound disabled in that case).
|
||||
+ SRAM word read handler was using incorrect byte order, fixed.
|
||||
|
||||
Changes in Cyclone 0.0086:
|
||||
+ Added missing CHK opcode handler (used by SeaQuest DSV).
|
||||
+ Added missing TAS opcode handler (Gargoyles,Bubba N Stix,...). As in real genesis,
|
||||
memory write-back phase is ignored (but can be enabled in config.h if needed).
|
||||
+ Added missing NBCD and TRAPV opcode handlers.
|
||||
+ Added missing addressing mode for CMP/EOR.
|
||||
+ Added some minor optimizations.
|
||||
- Removed 216 handlers for 2927 opcodes which were generated for invalid addressing modes.
|
||||
+ Fixed flags for ASL, NEG, NEGX, DIVU, ADDX, SUBX, ROXR.
|
||||
+ Bugs fixed in MOVEP, LINK, ADDQ, DIVS handlers.
|
||||
* Undocumented flags for CHK, ABCD, SBCD and NBCD are now emulated the same way as in Musashi.
|
||||
+ Added Uninitialized Interrupt emulation.
|
||||
+ Altered timing for about half of opcodes to match Musashi's.
|
||||
|
||||
0.80
|
||||
* Nearly all VDP code was rewritten in ARM asm. Gives ~10-25% performance
|
||||
increase (depends on game).
|
||||
* Optimized 32-column renderer not to render tiles offscreen, games which
|
||||
use 32-column display (like Shining Force) run ~50% faster.
|
||||
+ Added new "Alternative renderer", which gives another ~30-45% performance
|
||||
increase (in addition to mentioned above), but works only with some games,
|
||||
because it is missing some features (it uses tile-based rendering
|
||||
instead of default line-based and disables H-ints).
|
||||
+ Added "fit2" display mode for all FC gamers. It always uses 208x146 for
|
||||
P800 and 208x208 for all other phones.
|
||||
+ Added volume control for Motorolas (experimental).
|
||||
|
||||
VDP changes:
|
||||
+ Added support for vertical window (used by Vapor Trail, Mercs, GRIND
|
||||
Stormer and others).
|
||||
+ Added sprite masking (hiding), adds some speed.
|
||||
+ Added preliminary H counter emulation. Comix Zone and Sonic 3D Blast
|
||||
special stage are now playable.
|
||||
+ Added column based vertical scrolling (Gunstar Heroes battleship level,
|
||||
Sonic and Knuckles lava boss, etc).
|
||||
|
||||
Emulation changes:
|
||||
+ Re-added and improved Z80 faking when Z80 is disabled. Many games now can
|
||||
be played without enabling Z80 (Lost Vikings, Syndicate, etc), but some
|
||||
still need it (International Superstar Soccer Deluxe).
|
||||
* Improved ym2612 timers, Outrun music plays at correct speed, voices in
|
||||
Earthworm Jim play better, more games play sound.
|
||||
* I/O registers now remember their values (needed for Pirates! Gold)
|
||||
+ Added support for 6 button pad.
|
||||
|
||||
Changes in Cyclone 0.0083wip:
|
||||
+ Added missing CHK opcode (used by SeaQuest DSV).
|
||||
+ Added missing TAS opcode (Gargoyles). As in real genesis, write-back phase
|
||||
is ignored (but is enabled for other systems).
|
||||
|
||||
Backported stuff from Snes9x:
|
||||
* Fixed Pxxx jog up/down which were not working in game.
|
||||
+ Added an option to gzip save states to save space.
|
||||
+ The emulator now pauses whenever it is loosing focus, so it will now pause
|
||||
when alarm/ponecall/battery low/... windows come up.
|
||||
- Removed 'pause on phonecall' feature, as it is no longer needed.
|
||||
+ Video fix for asian A1000s.
|
||||
|
||||
0.70
|
||||
* Started using tools from "Symbian GCC Improvement Project", which give
|
||||
considerable speed increase (~4fps in "center 90" mode).
|
||||
* Rewrote some drawing routines in ARM assembly (gives ~6 more fps in
|
||||
"center 90" mode).
|
||||
* Minor improvement to 0 and 180 "fit" modes. Now they look slightly better
|
||||
and are faster.
|
||||
* Minor stability improvements (emulator is less likely to crash).
|
||||
+ Added some background for OSD text for better readability.
|
||||
+ Added Pal/NTSC detection. This is needed for proper sound speed.
|
||||
+ Implemented Reesy's DrZ80 Z80 emu. Made some changes to it with hope to make
|
||||
it faster.
|
||||
+ Implemented ym2612 emu from the MAME project. Runs well but sometimes sounds
|
||||
a bit weird. Could be a little faster, so made some changes too.
|
||||
+ Implemented SN76489 emu from the MAME project.
|
||||
+ Added two separate sound output methods (mediaserver and cmaudiofb) with
|
||||
autodetection (needs testing).
|
||||
* Fixed VDP DMA fill emulation (as described in Charles MacDonald's docs),
|
||||
fixes Contra and some other games.
|
||||
|
||||
0.301
|
||||
Launcher:
|
||||
* Launcher now starts emulation process from current directory,
|
||||
not from hardcoded paths.
|
||||
* Improved 'pause on call' feature, should hopefully work with Motorola phones.
|
||||
|
||||
0.30 (2006-01-07)
|
||||
Initial release based on fDave's code.
|
|
@ -0,0 +1,43 @@
|
|||
CC = x86_64-nt64-midipix-gcc
|
||||
|
||||
CCFLAGS:=-I. \
|
||||
-I../emulibc \
|
||||
-Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \
|
||||
-std=c99 -fomit-frame-pointer -fvisibility=hidden \
|
||||
-falign-functions=16 \
|
||||
-DLSB_FIRST -DNDEBUG -DEMU_F68K -D_USE_CZ80 \
|
||||
-O0 -g
|
||||
|
||||
TARGET = picodrive.wbx
|
||||
|
||||
LDFLAGS = -Wl,--dynamicbase,--export-all-symbols
|
||||
|
||||
ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
SRCS:=$(shell find $(ROOT_DIR) -type f -name '*.c')
|
||||
OBJ_DIR:=$(ROOT_DIR)/obj
|
||||
|
||||
_OBJS:=$(SRCS:.c=.o)
|
||||
OBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS))
|
||||
|
||||
$(OBJ_DIR)/%.o: %.c
|
||||
@mkdir -p $(@D)
|
||||
@$(CC) -c -o $@ $< $(CCFLAGS)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
.PHONY: clean all
|
||||
|
||||
$(TARGET).in: $(OBJS)
|
||||
@$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) ../emulibc/libemuhost.so
|
||||
|
||||
$(TARGET): $(TARGET).in
|
||||
# strip $< -o $@ -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104
|
||||
cp $< $@
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJ_DIR)
|
||||
rm -f $(TARGET).in
|
||||
rm -f $(TARGET)
|
||||
|
||||
#install:
|
||||
# $(CP) $(TARGET) $(DEST_$(ARCH))
|
|
@ -0,0 +1,154 @@
|
|||
# Makefile for PicoDrive (libretro)
|
||||
|
||||
ifeq ($(platform),)
|
||||
platform = unix
|
||||
ifeq ($(shell uname -a),)
|
||||
platform = win
|
||||
else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(shell uname -a)),)
|
||||
platform = osx
|
||||
else ifneq ($(findstring win,$(shell uname -a)),)
|
||||
platform = win
|
||||
endif
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
CXX ?= g++
|
||||
AS ?= as
|
||||
CC_AS ?= $(CC)
|
||||
CFLAGS ?=
|
||||
|
||||
TARGET_NAME := picodrive
|
||||
|
||||
asm_memory = 0
|
||||
asm_render = 0
|
||||
asm_ym2612 = 0
|
||||
asm_misc = 0
|
||||
asm_cdpico = 0
|
||||
asm_cdmemory = 0
|
||||
asm_mix = 0
|
||||
|
||||
ifeq ($(platform), unix)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
SHARED := -shared
|
||||
else ifeq ($(platform), osx)
|
||||
TARGET := $(TARGET_NAME)_libretro.dylib
|
||||
SHARED := -dynamiclib
|
||||
else ifeq ($(platform), ios)
|
||||
TARGET := $(TARGET_NAME)_libretro_ios.dylib
|
||||
SHARED := -dynamiclib
|
||||
|
||||
CC = clang -arch armv7 -isysroot $(IOSSDK) -miphoneos-version-min=5.0
|
||||
CXX = clang++ -arch armv7 -isysroot $(IOSSDK) -miphoneos-version-min=5.0
|
||||
CC_AS = perl ./tools/gas-preprocessor.pl $(CC) -miphoneos-version-min=5.0
|
||||
CFLAGS += -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon -marm
|
||||
ASFLAGS += -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon
|
||||
CFLAGS += -DIOS -miphoneos-version-min=5.0
|
||||
|
||||
ARCH := arm
|
||||
|
||||
use_cyclone = 0
|
||||
use_fame = 1
|
||||
use_drz80 = 0
|
||||
use_cz80 = 1
|
||||
use_sh2drc = 1
|
||||
use_svpdrc = 1
|
||||
|
||||
else ifeq ($(platform), ps3)
|
||||
TARGET := $(TARGET_NAME)_libretro_ps3.a
|
||||
CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
|
||||
AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe
|
||||
CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__
|
||||
else ifeq ($(platform), sncps3)
|
||||
TARGET := $(TARGET_NAME)_libretro_ps3.a
|
||||
CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe
|
||||
CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__
|
||||
else ifeq ($(platform), psl1ght)
|
||||
TARGET := $(TARGET_NAME)_libretro_psl1ght.a
|
||||
CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT)
|
||||
AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT)
|
||||
CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__
|
||||
else ifeq ($(platform), psp1)
|
||||
TARGET := $(TARGET_NAME)_libretro_psp1.a
|
||||
CC = psp-gcc$(EXE_EXT)
|
||||
AR = psp-ar$(EXE_EXT)
|
||||
CFLAGS += -DPSP -G0
|
||||
else ifeq ($(platform), xenon)
|
||||
TARGET := $(TARGET_NAME)_libretro_xenon360.a
|
||||
CC = xenon-gcc$(EXE_EXT)
|
||||
AR = xenon-ar$(EXE_EXT)
|
||||
CFLAGS += -D__LIBXENON__ -m32 -D__ppc__
|
||||
else ifeq ($(platform), ngc)
|
||||
TARGET := $(TARGET_NAME)_libretro_ngc.a
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
|
||||
CFLAGS += -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__
|
||||
else ifeq ($(platform), wii)
|
||||
TARGET := libretro_$(TARGET_NAME)_wii.a
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
|
||||
CFLAGS += -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__
|
||||
else ifeq ($(platform), qnx)
|
||||
TARGET := $(TARGET_NAME)_libretro_qnx.so
|
||||
CC = qcc -Vgcc_ntoarmv7le
|
||||
CC_AS = $(CC)
|
||||
CFLAGS += -DBASE_ADDR_FIXED=0 -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon -mfloat-abi=softfp
|
||||
ASFLAGS += -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
|
||||
|
||||
ARCH = arm
|
||||
ARM_ASM = 1
|
||||
else ifneq (,$(findstring armv,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
SHARED := -shared -Wl,--no-undefined
|
||||
ifneq (,$(findstring cortexa8,$(platform)))
|
||||
CFLAGS += -marm -mcpu=cortex-a8
|
||||
ASFLAGS += -mcpu=cortex-a8
|
||||
else ifneq (,$(findstring cortexa9,$(platform)))
|
||||
CFLAGS += -marm -mcpu=cortex-a9
|
||||
ASFLAGS += -mcpu=cortex-a9
|
||||
endif
|
||||
CFLAGS += -marm
|
||||
ifneq (,$(findstring neon,$(platform)))
|
||||
CFLAGS += -mfpu=neon
|
||||
ASFLAGS += -mfpu=neon
|
||||
endif
|
||||
ifneq (,$(findstring softfloat,$(platform)))
|
||||
CFLAGS += -mfloat-abi=softfp
|
||||
ASFLAGS += -mfloat-abi=softfp
|
||||
else ifneq (,$(findstring hardfloat,$(platform)))
|
||||
CFLAGS += -mfloat-abi=hard
|
||||
ASFLAGS += -mfloat-abi=hard
|
||||
endif
|
||||
ifneq (,$(findstring armasm,$(platform)))
|
||||
ARM_ASM = 1
|
||||
endif
|
||||
ARCH = arm
|
||||
else
|
||||
TARGET := $(TARGET_NAME)_libretro.dll
|
||||
CC = gcc
|
||||
LD_FLAGS := -fPIC
|
||||
SHARED := -shared -static-libgcc -static-libstdc++
|
||||
CFLAGS += -D__WIN32__ -D__WIN32_LIBRETRO__
|
||||
endif
|
||||
|
||||
ifeq ($(ARM_ASM),1)
|
||||
asm_memory = 1
|
||||
asm_render = 1
|
||||
asm_ym2612 = 1
|
||||
asm_misc = 1
|
||||
asm_cdpico = 1
|
||||
asm_cdmemory = 1
|
||||
asm_mix = 1
|
||||
endif
|
||||
|
||||
CFLAGS += -fPIC
|
||||
LDLIBS += -lm
|
||||
SHARED ?= -shared
|
||||
LDFLAGS += $(SHARED)
|
||||
|
||||
PLATFORM = libretro
|
||||
NO_CONFIG_MAK = yes
|
||||
|
||||
include Makefile
|
|
@ -0,0 +1,227 @@
|
|||
TARGET ?= PicoDrive
|
||||
CFLAGS += -Wall -ggdb -falign-functions=2
|
||||
CFLAGS += -I.
|
||||
ifndef DEBUG
|
||||
CFLAGS += -O3 -DNDEBUG -ffunction-sections
|
||||
ifeq ($(findstring clang,$(CC)),)
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
endif
|
||||
endif
|
||||
#CFLAGS += -DEVT_LOG
|
||||
#CFLAGS += -DDRC_CMP
|
||||
#cpu_cmp = 1
|
||||
#drc_debug = 7
|
||||
#profile = 1
|
||||
|
||||
|
||||
all: config.mak target_
|
||||
|
||||
ifndef NO_CONFIG_MAK
|
||||
ifneq ($(wildcard config.mak),)
|
||||
config.mak: ./configure
|
||||
@echo $@ is out-of-date, running configure
|
||||
@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh
|
||||
include config.mak
|
||||
else
|
||||
config.mak:
|
||||
@echo "Please run ./configure before running make!"
|
||||
@exit 1
|
||||
endif
|
||||
else # NO_CONFIG_MAK
|
||||
config.mak:
|
||||
endif
|
||||
|
||||
# default settings
|
||||
ifeq "$(ARCH)" "arm"
|
||||
use_cyclone ?= 1
|
||||
use_drz80 ?= 1
|
||||
use_sh2drc ?= 1
|
||||
use_svpdrc ?= 1
|
||||
|
||||
asm_memory ?= 1
|
||||
asm_render ?= 1
|
||||
asm_ym2612 ?= 1
|
||||
asm_misc ?= 1
|
||||
asm_cdpico ?= 1
|
||||
asm_cdmemory ?= 1
|
||||
asm_mix ?= 1
|
||||
else # if not arm
|
||||
use_fame ?= 1
|
||||
use_cz80 ?= 1
|
||||
endif
|
||||
|
||||
-include Makefile.local
|
||||
|
||||
ifneq "$(use_cyclone)" "1"
|
||||
# due to CPU stop flag access
|
||||
asm_cdpico = 0
|
||||
asm_cdmemory = 0
|
||||
endif
|
||||
|
||||
ifeq "$(PLATFORM)" "opendingux"
|
||||
opk: $(TARGET).opk
|
||||
|
||||
$(TARGET).opk: $(TARGET)
|
||||
$(RM) -rf .opk_data
|
||||
cp -r platform/opendingux/data .opk_data
|
||||
cp $< .opk_data/PicoDrive
|
||||
$(STRIP) .opk_data/PicoDrive
|
||||
mksquashfs .opk_data $@ -all-root -noappend -no-exports -no-xattrs
|
||||
|
||||
OBJS += platform/opendingux/inputmap.o
|
||||
|
||||
# OpenDingux is a generic platform, really.
|
||||
PLATFORM := generic
|
||||
endif
|
||||
ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","rpi1" "rpi2"))
|
||||
CFLAGS += -DHAVE_GLES -DRASPBERRY
|
||||
CFLAGS += -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads/ -I/opt/vc/include/interface/vmcs_host/linux/
|
||||
LDFLAGS += -ldl -lbcm_host -L/opt/vc/lib -lEGL -lGLESv2
|
||||
OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
|
||||
OBJS += platform/common/plat_sdl.o
|
||||
OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
|
||||
OBJS += platform/libpicofe/plat_dummy.o
|
||||
OBJS += platform/libpicofe/gl.o
|
||||
OBJS += platform/libpicofe/gl_platform.o
|
||||
USE_FRONTEND = 1
|
||||
endif
|
||||
ifeq "$(PLATFORM)" "generic"
|
||||
OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
|
||||
OBJS += platform/common/plat_sdl.o
|
||||
OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
|
||||
OBJS += platform/libpicofe/plat_dummy.o
|
||||
USE_FRONTEND = 1
|
||||
endif
|
||||
ifeq "$(PLATFORM)" "pandora"
|
||||
platform/common/menu_pico.o: CFLAGS += -DPANDORA
|
||||
platform/libpicofe/linux/plat.o: CFLAGS += -DPANDORA
|
||||
OBJS += platform/pandora/plat.o
|
||||
OBJS += platform/pandora/asm_utils.o
|
||||
OBJS += platform/common/arm_utils.o
|
||||
OBJS += platform/libpicofe/linux/fbdev.o
|
||||
OBJS += platform/libpicofe/linux/xenv.o
|
||||
OBJS += platform/libpicofe/pandora/plat.o
|
||||
USE_FRONTEND = 1
|
||||
endif
|
||||
ifeq "$(PLATFORM)" "gp2x"
|
||||
OBJS += platform/common/arm_utils.o
|
||||
OBJS += platform/libpicofe/gp2x/in_gp2x.o
|
||||
OBJS += platform/libpicofe/gp2x/soc.o
|
||||
OBJS += platform/libpicofe/gp2x/soc_mmsp2.o
|
||||
OBJS += platform/libpicofe/gp2x/soc_pollux.o
|
||||
OBJS += platform/libpicofe/gp2x/plat.o
|
||||
OBJS += platform/libpicofe/gp2x/pollux_set.o
|
||||
OBJS += platform/gp2x/940ctl.o
|
||||
OBJS += platform/gp2x/plat.o
|
||||
OBJS += platform/gp2x/emu.o
|
||||
OBJS += platform/gp2x/vid_mmsp2.o
|
||||
OBJS += platform/gp2x/vid_pollux.o
|
||||
OBJS += platform/gp2x/warm.o
|
||||
USE_FRONTEND = 1
|
||||
PLATFORM_MP3 = 1
|
||||
endif
|
||||
ifeq "$(PLATFORM)" "libretro"
|
||||
OBJS += platform/libretro.o
|
||||
endif
|
||||
|
||||
ifeq "$(USE_FRONTEND)" "1"
|
||||
|
||||
# common
|
||||
OBJS += platform/common/main.o platform/common/emu.o \
|
||||
platform/common/menu_pico.o platform/common/config_file.o
|
||||
|
||||
# libpicofe
|
||||
OBJS += platform/libpicofe/input.o platform/libpicofe/readpng.o \
|
||||
platform/libpicofe/fonts.o platform/libpicofe/linux/in_evdev.o \
|
||||
platform/libpicofe/linux/plat.o
|
||||
|
||||
# libpicofe - sound
|
||||
OBJS += platform/libpicofe/sndout.o
|
||||
ifneq ($(findstring oss,$(SOUND_DRIVERS)),)
|
||||
platform/libpicofe/sndout.o: CFLAGS += -DHAVE_OSS
|
||||
OBJS += platform/libpicofe/linux/sndout_oss.o
|
||||
endif
|
||||
ifneq ($(findstring alsa,$(SOUND_DRIVERS)),)
|
||||
platform/libpicofe/sndout.o: CFLAGS += -DHAVE_ALSA
|
||||
OBJS += platform/libpicofe/linux/sndout_alsa.o
|
||||
endif
|
||||
ifneq ($(findstring sdl,$(SOUND_DRIVERS)),)
|
||||
platform/libpicofe/sndout.o: CFLAGS += -DHAVE_SDL
|
||||
OBJS += platform/libpicofe/sndout_sdl.o
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "arm"
|
||||
OBJS += platform/libpicofe/arm_linux.o
|
||||
endif
|
||||
|
||||
endif # USE_FRONTEND
|
||||
|
||||
OBJS += platform/common/mp3.o
|
||||
ifeq "$(PLATFORM_MP3)" "1"
|
||||
else ifeq "$(HAVE_LIBAVCODEC)" "1"
|
||||
OBJS += platform/common/mp3_libavcodec.o
|
||||
else
|
||||
OBJS += platform/common/mp3_dummy.o
|
||||
endif
|
||||
|
||||
ifeq "$(PLATFORM)" "libretro"
|
||||
# zlib
|
||||
OBJS += zlib/gzio.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o \
|
||||
zlib/deflate.o zlib/crc32.o zlib/adler32.o zlib/zutil.o zlib/compress.o zlib/uncompr.o
|
||||
CFLAGS += -Izlib
|
||||
endif
|
||||
# unzip
|
||||
OBJS += unzip/unzip.o
|
||||
|
||||
|
||||
include platform/common/common.mak
|
||||
|
||||
OBJS += $(OBJS_COMMON)
|
||||
CFLAGS += $(addprefix -D,$(DEFINES))
|
||||
|
||||
ifneq ($(findstring gcc,$(CC)),)
|
||||
LDFLAGS += -Wl,-Map=$(TARGET).map
|
||||
endif
|
||||
|
||||
|
||||
target_: $(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGET) $(OBJS)
|
||||
$(RM) -r .opk_data
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
pprof: platform/linux/pprof.c
|
||||
$(CC) -O2 -ggdb -DPPROF -DPPROF_TOOL -I../../ -I. $^ -o $@
|
||||
|
||||
tools/textfilter: tools/textfilter.c
|
||||
make -C tools/ textfilter
|
||||
|
||||
.s.o:
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# special flags - perhaps fix this someday instead?
|
||||
pico/draw.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/draw2.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/mode4.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/cd/memory.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/cd/cd_file.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/cd/pcm.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/cd/LC89510.o: CFLAGS += -fno-strict-aliasing
|
||||
pico/cd/gfx_cd.o: CFLAGS += -fno-strict-aliasing
|
||||
|
||||
# fame needs ~2GB of RAM to compile on gcc 4.8
|
||||
# on x86, this is reduced by ~300MB when debug info is off (but not on ARM)
|
||||
# not using O3 and -fno-expensive-optimizations seems to also help, but you may
|
||||
# want to remove this stuff for better performance if your compiler can handle it
|
||||
cpu/fame/famec.o: CFLAGS += -g0 -O2 -fno-expensive-optimizations
|
||||
|
||||
# random deps
|
||||
pico/carthw/svp/compiler.o : cpu/drc/emit_$(ARCH).c
|
||||
cpu/sh2/compiler.o : cpu/drc/emit_$(ARCH).c
|
||||
cpu/sh2/mame/sh2pico.o : cpu/sh2/mame/sh2.c
|
||||
pico/pico.o pico/cd/mcd.o pico/32x/32x.o : pico/pico_cmn.c pico/pico_int.h
|
||||
pico/memory.o pico/cd/memory.o pico/32x/memory.o : pico/pico_int.h pico/memory.h
|
||||
cpu/fame/famec.o: cpu/fame/famec.c cpu/fame/famec_opcodes.h
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
This is yet another Megadrive / Genesis / Sega CD / Mega CD / 32X / SMS
|
||||
emulator, which was written having ARM-based handheld devices in mind
|
||||
(such as smartphones and handheld consoles like GP2X and Pandora),
|
||||
but also runs on non-ARM little-endian hardware too.
|
||||
|
||||
The emulator is heavily optimized for ARM, features assembly cores for
|
||||
68k, Z80 and VDP chip emulation, also has dynamic recompilers for SH2 and
|
||||
SSP16 (for 32X and SVP emulation). It was started by Dave (aka fdave,
|
||||
finalburn author) as basic Genesis/Megadrive emulator for Pocket PC,
|
||||
then taken over and expanded by notaz.
|
||||
|
||||
PicoDrive was the first emulator ever to properly emulate Virtua Racing and
|
||||
it's SVP chip.
|
||||
|
||||
How to compile on Raspbian Wheezy:
|
||||
|
||||
export CC=gcc-4.8
|
||||
export CXX=g++-4.8
|
||||
./configure --platform=rpi2
|
||||
make
|
|
@ -0,0 +1,153 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../emulibc/emulibc.h"
|
||||
#include "../emulibc/waterboxcore.h"
|
||||
#include "pico/pico.h"
|
||||
#include "pico/cd/cue.h"
|
||||
|
||||
void lprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
int PicoCartResize(int newsize)
|
||||
{
|
||||
// TODO: change boards that use this to store their extra data elsewhere
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PicoCdCheck(const char *fname_in, int *pregion)
|
||||
{
|
||||
return CIT_NOT_CD;
|
||||
}
|
||||
|
||||
cue_data_t *cue_parse(const char *fname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pm_file *pm_open(const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f)
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
pm_file *ret = calloc(1, sizeof(*ret));
|
||||
ret->file = f;
|
||||
ret->size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
ret->type = PMT_UNCOMPRESSED;
|
||||
return ret;
|
||||
}
|
||||
size_t pm_read(void *ptr, size_t bytes, pm_file *stream)
|
||||
{
|
||||
return fread(ptr, 1, bytes, (FILE *)stream->file);
|
||||
}
|
||||
int pm_seek(pm_file *stream, long offset, int whence)
|
||||
{
|
||||
return fseek((FILE *)stream->file, offset, whence);
|
||||
}
|
||||
int pm_close(pm_file *fp)
|
||||
{
|
||||
int ret = fclose((FILE *)fp->file);
|
||||
fp->file = NULL;
|
||||
free(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int video_start_line;
|
||||
static int video_line_count;
|
||||
static int video_width;
|
||||
static uint16_t* video_buffer;
|
||||
|
||||
void emu_video_mode_change(int start_line, int line_count, int is_32cols)
|
||||
{
|
||||
video_start_line = start_line;
|
||||
video_line_count = line_count;
|
||||
video_width = is_32cols ? 256 : 320;
|
||||
}
|
||||
|
||||
// "switch to 16bpp mode?"
|
||||
void emu_32x_startup(void)
|
||||
{
|
||||
}
|
||||
|
||||
int mp3_get_bitrate(void *f, int size) { return 0; }
|
||||
void mp3_start_play(void *f, int pos) {}
|
||||
void mp3_update(int *buffer, int length, int stereo) {}
|
||||
|
||||
ECL_EXPORT int Init(void)
|
||||
{
|
||||
video_buffer = alloc_invisible(512 * 512 * sizeof(uint16_t));
|
||||
|
||||
PicoInit();
|
||||
PicoSetInputDevice(0, PICO_INPUT_PAD_6BTN);
|
||||
PicoSetInputDevice(1, PICO_INPUT_PAD_6BTN);
|
||||
PicoDrawSetOutBuf(video_buffer, 512 * sizeof(uint16_t));
|
||||
|
||||
if (PicoLoadMedia("romfile.md", NULL, NULL, NULL, PM_MD_CART) != PM_MD_CART)
|
||||
return 0;
|
||||
PicoDrawSetOutFormat(PDF_RGB555, 0); // TODO: what is "use_32x_line_mode"?
|
||||
PicoPower();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FrameInfo b;
|
||||
} MyFrameInfo;
|
||||
|
||||
static void Blit(const uint16_t *src, FrameInfo *f)
|
||||
{
|
||||
f->Width = video_width;
|
||||
f->Height = video_line_count;
|
||||
uint8_t *dst = (uint8_t *)f->VideoBuffer;
|
||||
src += 512 * video_start_line;
|
||||
if (video_width == 256)
|
||||
src += 32;
|
||||
|
||||
for (int j = 0; j < video_line_count; j++)
|
||||
{
|
||||
for (int i = 0; i < video_width; i++)
|
||||
{
|
||||
uint16_t c = src[i];
|
||||
*dst++ = c << 3 & 0xf8 | c >> 2 & 7;
|
||||
*dst++ = c >> 3 & 0xfa | c >> 9 & 3;
|
||||
*dst++ = c >> 8 & 0xf8 | c >> 13 & 7;
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
src += 512;
|
||||
}
|
||||
}
|
||||
|
||||
ECL_EXPORT void FrameAdvance(MyFrameInfo *f)
|
||||
{
|
||||
PicoLoopPrepare();
|
||||
PicoFrame();
|
||||
Blit(video_buffer, &f->b);
|
||||
f->b.Samples = 735; // TODO
|
||||
}
|
||||
|
||||
static uint8_t dumbo[16];
|
||||
ECL_EXPORT void GetMemoryAreas(MemoryArea *m)
|
||||
{
|
||||
m[0].Data = dumbo;
|
||||
m[0].Name = "TODO";
|
||||
m[0].Size = 16;
|
||||
m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_PRIMARY;
|
||||
}
|
||||
|
||||
ECL_EXPORT void SetInputCallback(void (*callback)(void))
|
||||
{
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,488 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 (Z80 CPU emulator) version 0.9
|
||||
* Compiled with Dev-C++
|
||||
* Copyright 2004-2005 St<EFBFBD>phane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cz80.h"
|
||||
|
||||
#if PICODRIVE_HACKS
|
||||
#include <pico/memory.h>
|
||||
#endif
|
||||
|
||||
#ifndef ALIGN_DATA
|
||||
#ifdef _MSC_VER
|
||||
#define ALIGN_DATA
|
||||
#define inline
|
||||
#undef CZ80_USE_JUMPTABLE
|
||||
#define CZ80_USE_JUMPTABLE 0
|
||||
#else
|
||||
#define ALIGN_DATA __attribute__((aligned(4)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CF 0x01
|
||||
#define NF 0x02
|
||||
#define PF 0x04
|
||||
#define VF PF
|
||||
#define XF 0x08
|
||||
#define HF 0x10
|
||||
#define YF 0x20
|
||||
#define ZF 0x40
|
||||
#define SF 0x80
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
<EFBFBD>}<EFBFBD>N<EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
|
||||
#include "cz80macro.h"
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
<EFBFBD>O<EFBFBD><EFBFBD><EFBFBD>[<EFBFBD>o<EFBFBD><EFBFBD><EFBFBD>\<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
|
||||
cz80_struc ALIGN_DATA CZ80;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
<EFBFBD><EFBFBD><EFBFBD>[<EFBFBD>J<EFBFBD><EFBFBD><EFBFBD>ϐ<EFBFBD>
|
||||
******************************************************************************/
|
||||
|
||||
static UINT8 ALIGN_DATA cz80_bad_address[1 << CZ80_FETCH_SFT];
|
||||
|
||||
static UINT8 ALIGN_DATA SZ[256];
|
||||
static UINT8 ALIGN_DATA SZP[256];
|
||||
static UINT8 ALIGN_DATA SZ_BIT[256];
|
||||
static UINT8 ALIGN_DATA SZHV_inc[256];
|
||||
static UINT8 ALIGN_DATA SZHV_dec[256];
|
||||
#if CZ80_BIG_FLAGS_ARRAY
|
||||
static UINT8 ALIGN_DATA SZHVC_add[2*256*256];
|
||||
static UINT8 ALIGN_DATA SZHVC_sub[2*256*256];
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
<EFBFBD><EFBFBD><EFBFBD>[<EFBFBD>J<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD><EFBFBD><EFBFBD>荞<EFBFBD>݃R<EFBFBD>[<EFBFBD><EFBFBD><EFBFBD>o<EFBFBD>b<EFBFBD>N
|
||||
--------------------------------------------------------*/
|
||||
|
||||
static INT32 Cz80_Interrupt_Callback(INT32 line)
|
||||
{
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
CZ80<EFBFBD>C<EFBFBD><EFBFBD><EFBFBD>^<EFBFBD>t<EFBFBD>F<EFBFBD>[<EFBFBD>X<EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
|
||||
/*--------------------------------------------------------
|
||||
CPU<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Init(cz80_struc *CPU)
|
||||
{
|
||||
UINT32 i, j, p;
|
||||
#if CZ80_BIG_FLAGS_ARRAY
|
||||
int oldval, newval, val;
|
||||
UINT8 *padd, *padc, *psub, *psbc;
|
||||
#endif
|
||||
|
||||
memset(CPU, 0, sizeof(cz80_struc));
|
||||
|
||||
memset(cz80_bad_address, 0xff, sizeof(cz80_bad_address));
|
||||
|
||||
for (i = 0; i < CZ80_FETCH_BANK; i++)
|
||||
{
|
||||
CPU->Fetch[i] = (FPTR)cz80_bad_address;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
CPU->OPFetch[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// flags tables initialisation
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
SZ[i] = i & (SF | YF | XF);
|
||||
if (!i) SZ[i] |= ZF;
|
||||
|
||||
SZ_BIT[i] = i & (SF | YF | XF);
|
||||
if (!i) SZ_BIT[i] |= ZF | PF;
|
||||
|
||||
for (j = 0, p = 0; j < 8; j++) if (i & (1 << j)) p++;
|
||||
SZP[i] = SZ[i];
|
||||
if (!(p & 1)) SZP[i] |= PF;
|
||||
|
||||
SZHV_inc[i] = SZ[i];
|
||||
if(i == 0x80) SZHV_inc[i] |= VF;
|
||||
if((i & 0x0f) == 0x00) SZHV_inc[i] |= HF;
|
||||
|
||||
SZHV_dec[i] = SZ[i] | NF;
|
||||
if (i == 0x7f) SZHV_dec[i] |= VF;
|
||||
if ((i & 0x0f) == 0x0f) SZHV_dec[i] |= HF;
|
||||
}
|
||||
|
||||
#if CZ80_BIG_FLAGS_ARRAY
|
||||
padd = &SZHVC_add[ 0*256];
|
||||
padc = &SZHVC_add[256*256];
|
||||
psub = &SZHVC_sub[ 0*256];
|
||||
psbc = &SZHVC_sub[256*256];
|
||||
|
||||
for (oldval = 0; oldval < 256; oldval++)
|
||||
{
|
||||
for (newval = 0; newval < 256; newval++)
|
||||
{
|
||||
/* add or adc w/o carry set */
|
||||
val = newval - oldval;
|
||||
*padd = (newval) ? ((newval & 0x80) ? SF : 0) : ZF;
|
||||
*padd |= (newval & (YF | XF)); /* undocumented flag bits 5+3 */
|
||||
if ((newval & 0x0f) < (oldval & 0x0f)) *padd |= HF;
|
||||
if (newval < oldval ) *padd |= CF;
|
||||
if ((val ^ oldval ^ 0x80) & (val ^ newval) & 0x80) *padd |= VF;
|
||||
padd++;
|
||||
|
||||
/* adc with carry set */
|
||||
val = newval - oldval - 1;
|
||||
*padc = (newval) ? ((newval & 0x80) ? SF : 0) : ZF;
|
||||
*padc |= (newval & (YF | XF)); /* undocumented flag bits 5+3 */
|
||||
if ((newval & 0x0f) <= (oldval & 0x0f)) *padc |= HF;
|
||||
if (newval <= oldval) *padc |= CF;
|
||||
if ((val ^ oldval ^ 0x80) & (val ^ newval) & 0x80) *padc |= VF;
|
||||
padc++;
|
||||
|
||||
/* cp, sub or sbc w/o carry set */
|
||||
val = oldval - newval;
|
||||
*psub = NF | ((newval) ? ((newval & 0x80) ? SF : 0) : ZF);
|
||||
*psub |= (newval & (YF | XF)); /* undocumented flag bits 5+3 */
|
||||
if ((newval & 0x0f) > (oldval & 0x0f)) *psub |= HF;
|
||||
if (newval > oldval) *psub |= CF;
|
||||
if ((val^oldval) & (oldval^newval) & 0x80) *psub |= VF;
|
||||
psub++;
|
||||
|
||||
/* sbc with carry set */
|
||||
val = oldval - newval - 1;
|
||||
*psbc = NF | ((newval) ? ((newval & 0x80) ? SF : 0) : ZF);
|
||||
*psbc |= (newval & (YF | XF)); /* undocumented flag bits 5+3 */
|
||||
if ((newval & 0x0f) >= (oldval & 0x0f)) *psbc |= HF;
|
||||
if (newval >= oldval) *psbc |= CF;
|
||||
if ((val ^ oldval) & (oldval^newval) & 0x80) *psbc |= VF;
|
||||
psbc++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CPU->pzR8[0] = &zB;
|
||||
CPU->pzR8[1] = &zC;
|
||||
CPU->pzR8[2] = &zD;
|
||||
CPU->pzR8[3] = &zE;
|
||||
CPU->pzR8[4] = &zH;
|
||||
CPU->pzR8[5] = &zL;
|
||||
CPU->pzR8[6] = &zF; // <20><><EFBFBD><EFBFBD><EFBFBD>̓s<CC93><73><EFBFBD><EFBFBD>AA<41>Ɠ<EFBFBD><C693><EFBFBD>ւ<EFBFBD>
|
||||
CPU->pzR8[7] = &zA; // <20><><EFBFBD><EFBFBD><EFBFBD>̓s<CC93><73><EFBFBD><EFBFBD>AF<41>Ɠ<EFBFBD><C693><EFBFBD>ւ<EFBFBD>
|
||||
|
||||
CPU->pzR16[0] = pzBC;
|
||||
CPU->pzR16[1] = pzDE;
|
||||
CPU->pzR16[2] = pzHL;
|
||||
CPU->pzR16[3] = pzAF;
|
||||
|
||||
zIX = zIY = 0xffff;
|
||||
zF = ZF;
|
||||
|
||||
CPU->Interrupt_Callback = Cz80_Interrupt_Callback;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
CPU<EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD>b<EFBFBD>g
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Reset(cz80_struc *CPU)
|
||||
{
|
||||
memset(CPU, 0, (FPTR)&CPU->BasePC - (FPTR)CPU);
|
||||
Cz80_Set_Reg(CPU, CZ80_PC, 0);
|
||||
}
|
||||
|
||||
/* */
|
||||
#if PICODRIVE_HACKS
|
||||
static inline unsigned char picodrive_read(unsigned short a)
|
||||
{
|
||||
uptr v = z80_read_map[a >> Z80_MEM_SHIFT];
|
||||
if (map_flag_set(v))
|
||||
return ((z80_read_f *)(v << 1))(a);
|
||||
return *(unsigned char *)((v << 1) + a);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------
|
||||
CPU<EFBFBD><EFBFBD><EFBFBD>s
|
||||
--------------------------------------------------------*/
|
||||
|
||||
INT32 Cz80_Exec(cz80_struc *CPU, INT32 cycles)
|
||||
{
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
#include "cz80jmp.inc"
|
||||
#endif
|
||||
|
||||
FPTR PC;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
FPTR OPBase;
|
||||
#endif
|
||||
UINT32 Opcode;
|
||||
UINT32 adr = 0;
|
||||
UINT32 res;
|
||||
UINT32 val;
|
||||
int afterEI = 0;
|
||||
union16 *data;
|
||||
|
||||
PC = CPU->PC;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
OPBase = CPU->OPBase;
|
||||
#endif
|
||||
CPU->ICount = cycles - CPU->ExtraCycles;
|
||||
CPU->ExtraCycles = 0;
|
||||
|
||||
if (!CPU->HaltState)
|
||||
{
|
||||
Cz80_Exec:
|
||||
if (CPU->ICount > 0)
|
||||
{
|
||||
Cz80_Exec_nocheck:
|
||||
data = pzHL;
|
||||
Opcode = READ_OP();
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR++;
|
||||
#endif
|
||||
#include "cz80_op.inc"
|
||||
}
|
||||
|
||||
if (afterEI)
|
||||
{
|
||||
afterEI = 0;
|
||||
Cz80_Check_Interrupt:
|
||||
if (CPU->IRQState != CLEAR_LINE)
|
||||
{
|
||||
CHECK_INT
|
||||
CPU->ICount -= CPU->ExtraCycles;
|
||||
CPU->ExtraCycles = 0;
|
||||
}
|
||||
goto Cz80_Exec;
|
||||
}
|
||||
}
|
||||
else CPU->ICount = 0;
|
||||
|
||||
Cz80_Exec_End:
|
||||
CPU->PC = PC;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
CPU->OPBase = OPBase;
|
||||
#endif
|
||||
cycles -= CPU->ICount;
|
||||
#if !CZ80_EMULATE_R_EXACTLY
|
||||
zR = (zR + (cycles >> 2)) & 0x7f;
|
||||
#endif
|
||||
|
||||
return cycles;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD><EFBFBD><EFBFBD>荞<EFBFBD>ݏ<EFBFBD><EFBFBD><EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_IRQ(cz80_struc *CPU, INT32 line, INT32 state)
|
||||
{
|
||||
if (line == IRQ_LINE_NMI)
|
||||
{
|
||||
zIFF1 = 0;
|
||||
CPU->ExtraCycles += 11;
|
||||
CPU->HaltState = 0;
|
||||
PUSH_16(CPU->PC - CPU->BasePC)
|
||||
Cz80_Set_Reg(CPU, CZ80_PC, 0x66);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPU->IRQState = state;
|
||||
|
||||
if (state != CLEAR_LINE)
|
||||
{
|
||||
FPTR PC = CPU->PC;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
FPTR OPBase = CPU->OPBase;
|
||||
#endif
|
||||
|
||||
CPU->IRQLine = line;
|
||||
CHECK_INT
|
||||
CPU->PC = PC;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
CPU->OPBase = OPBase;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD><EFBFBD><EFBFBD>W<EFBFBD>X<EFBFBD>^<EFBFBD>擾
|
||||
--------------------------------------------------------*/
|
||||
|
||||
UINT32 Cz80_Get_Reg(cz80_struc *CPU, INT32 regnum)
|
||||
{
|
||||
switch (regnum)
|
||||
{
|
||||
case CZ80_PC: return (CPU->PC - CPU->BasePC);
|
||||
case CZ80_SP: return zSP;
|
||||
case CZ80_AF: return zAF;
|
||||
case CZ80_BC: return zBC;
|
||||
case CZ80_DE: return zDE;
|
||||
case CZ80_HL: return zHL;
|
||||
case CZ80_IX: return zIX;
|
||||
case CZ80_IY: return zIY;
|
||||
case CZ80_AF2: return zAF2;
|
||||
case CZ80_BC2: return zBC2;
|
||||
case CZ80_DE2: return zDE2;
|
||||
case CZ80_HL2: return zHL2;
|
||||
case CZ80_R: return zR;
|
||||
case CZ80_I: return zI;
|
||||
case CZ80_IM: return zIM;
|
||||
case CZ80_IFF1: return zIFF1;
|
||||
case CZ80_IFF2: return zIFF2;
|
||||
case CZ80_HALT: return CPU->HaltState;
|
||||
case CZ80_IRQ: return CPU->IRQState;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD><EFBFBD><EFBFBD>W<EFBFBD>X<EFBFBD>^<EFBFBD>ݒ<EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_Reg(cz80_struc *CPU, INT32 regnum, UINT32 val)
|
||||
{
|
||||
switch (regnum)
|
||||
{
|
||||
case CZ80_PC:
|
||||
CPU->BasePC = CPU->Fetch[val >> CZ80_FETCH_SFT];
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
CPU->OPBase = CPU->OPFetch[val >> CZ80_FETCH_SFT];
|
||||
#endif
|
||||
CPU->PC = val + CPU->BasePC;
|
||||
break;
|
||||
|
||||
case CZ80_SP: zSP = val; break;
|
||||
case CZ80_AF: zAF = val; break;
|
||||
case CZ80_BC: zBC = val; break;
|
||||
case CZ80_DE: zDE = val; break;
|
||||
case CZ80_HL: zHL = val; break;
|
||||
case CZ80_IX: zIX = val; break;
|
||||
case CZ80_IY: zIY = val; break;
|
||||
case CZ80_AF2: zAF2 = val; break;
|
||||
case CZ80_BC2: zBC2 = val; break;
|
||||
case CZ80_DE2: zDE2 = val; break;
|
||||
case CZ80_HL2: zHL2 = val; break;
|
||||
case CZ80_R: zR = val; break;
|
||||
case CZ80_I: zI = val; break;
|
||||
case CZ80_IM: zIM = val; break;
|
||||
case CZ80_IFF1: zIFF1 = val ? (1 << 2) : 0; break;
|
||||
case CZ80_IFF2: zIFF2 = val ? (1 << 2) : 0; break;
|
||||
case CZ80_HALT: CPU->HaltState = val; break;
|
||||
case CZ80_IRQ: CPU->IRQState = val; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD>t<EFBFBD>F<EFBFBD>b<EFBFBD>`<EFBFBD>A<EFBFBD>h<EFBFBD><EFBFBD><EFBFBD>X<EFBFBD>ݒ<EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_Fetch(cz80_struc *CPU, UINT32 low_adr, UINT32 high_adr, FPTR fetch_adr)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
i = low_adr >> CZ80_FETCH_SFT;
|
||||
j = high_adr >> CZ80_FETCH_SFT;
|
||||
fetch_adr -= i << CZ80_FETCH_SFT;
|
||||
|
||||
while (i <= j)
|
||||
{
|
||||
CPU->Fetch[i] = fetch_adr;
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
CPU->OPFetch[i] = 0;
|
||||
#endif
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD>t<EFBFBD>F<EFBFBD>b<EFBFBD>`<EFBFBD>A<EFBFBD>h<EFBFBD><EFBFBD><EFBFBD>X<EFBFBD>ݒ<EFBFBD> (<EFBFBD>Í<EFBFBD><EFBFBD><EFBFBD>ROM<EFBFBD>Ή<EFBFBD>)
|
||||
--------------------------------------------------------*/
|
||||
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
void Cz80_Set_Encrypt_Range(cz80_struc *CPU, UINT32 low_adr, UINT32 high_adr, UINT32 decrypted_rom)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
i = low_adr >> CZ80_FETCH_SFT;
|
||||
j = high_adr >> CZ80_FETCH_SFT;
|
||||
decrypted_rom -= i << CZ80_FETCH_SFT;
|
||||
|
||||
while (i <= j)
|
||||
{
|
||||
CPU->OPFetch[i] = (INT32)decrypted_rom - (INT32)CPU->Fetch[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[<EFBFBD>h/<EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>g<EFBFBD><EFBFBD><EFBFBD>ݒ<EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_ReadB(cz80_struc *CPU, UINT8 (*Func)(UINT32 address))
|
||||
{
|
||||
CPU->Read_Byte = Func;
|
||||
}
|
||||
|
||||
void Cz80_Set_WriteB(cz80_struc *CPU, void (*Func)(UINT32 address, UINT8 data))
|
||||
{
|
||||
CPU->Write_Byte = Func;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD>|<EFBFBD>[<EFBFBD>g<EFBFBD><EFBFBD><EFBFBD>[<EFBFBD>h/<EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>g<EFBFBD><EFBFBD><EFBFBD>ݒ<EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_INPort(cz80_struc *CPU, UINT8 (*Func)(UINT16 port))
|
||||
{
|
||||
CPU->IN_Port = Func;
|
||||
}
|
||||
|
||||
void Cz80_Set_OUTPort(cz80_struc *CPU, void (*Func)(UINT16 port, UINT8 value))
|
||||
{
|
||||
CPU->OUT_Port = Func;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
<EFBFBD>R<EFBFBD>[<EFBFBD><EFBFBD><EFBFBD>o<EFBFBD>b<EFBFBD>N<EFBFBD><EFBFBD><EFBFBD>ݒ<EFBFBD>
|
||||
--------------------------------------------------------*/
|
||||
|
||||
void Cz80_Set_IRQ_Callback(cz80_struc *CPU, INT32 (*Func)(INT32 irqline))
|
||||
{
|
||||
CPU->Interrupt_Callback = Func;
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 (Z80 CPU emulator) version 0.9
|
||||
* Compiled with Dev-C++
|
||||
* Copyright 2004-2005 Stéphane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CZ80_H
|
||||
#define CZ80_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/******************************/
|
||||
/* Compiler dependant defines */
|
||||
/******************************/
|
||||
|
||||
#ifndef UINT8
|
||||
#define UINT8 unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef INT8
|
||||
#define INT8 signed char
|
||||
#endif
|
||||
|
||||
#ifndef UINT16
|
||||
#define UINT16 unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef INT16
|
||||
#define INT16 signed short
|
||||
#endif
|
||||
|
||||
#ifndef UINT32
|
||||
#define UINT32 unsigned int
|
||||
#endif
|
||||
|
||||
#ifndef INT32
|
||||
#define INT32 signed int
|
||||
#endif
|
||||
|
||||
#ifndef FPTR
|
||||
#define FPTR unsigned long
|
||||
#endif
|
||||
|
||||
/*************************************/
|
||||
/* Z80 core Structures & definitions */
|
||||
/*************************************/
|
||||
|
||||
#define CZ80_FETCH_BITS 4 // [4-12] default = 8
|
||||
|
||||
#define CZ80_FETCH_SFT (16 - CZ80_FETCH_BITS)
|
||||
#define CZ80_FETCH_BANK (1 << CZ80_FETCH_BITS)
|
||||
|
||||
#define PICODRIVE_HACKS 1
|
||||
#define CZ80_LITTLE_ENDIAN 1
|
||||
#define CZ80_USE_JUMPTABLE 1
|
||||
#define CZ80_BIG_FLAGS_ARRAY 1
|
||||
//#ifdef BUILD_CPS1PSP
|
||||
//#define CZ80_ENCRYPTED_ROM 1
|
||||
//#else
|
||||
#define CZ80_ENCRYPTED_ROM 0
|
||||
//#endif
|
||||
#define CZ80_EMULATE_R_EXACTLY 1
|
||||
|
||||
#define zR8(A) (*CPU->pzR8[A])
|
||||
#define zR16(A) (CPU->pzR16[A]->W)
|
||||
|
||||
#define pzAF &(CPU->AF)
|
||||
#define zAF CPU->AF.W
|
||||
#define zlAF CPU->AF.B.L
|
||||
#define zhAF CPU->AF.B.H
|
||||
#define zA zhAF
|
||||
#define zF zlAF
|
||||
|
||||
#define pzBC &(CPU->BC)
|
||||
#define zBC CPU->BC.W
|
||||
#define zlBC CPU->BC.B.L
|
||||
#define zhBC CPU->BC.B.H
|
||||
#define zB zhBC
|
||||
#define zC zlBC
|
||||
|
||||
#define pzDE &(CPU->DE)
|
||||
#define zDE CPU->DE.W
|
||||
#define zlDE CPU->DE.B.L
|
||||
#define zhDE CPU->DE.B.H
|
||||
#define zD zhDE
|
||||
#define zE zlDE
|
||||
|
||||
#define pzHL &(CPU->HL)
|
||||
#define zHL CPU->HL.W
|
||||
#define zlHL CPU->HL.B.L
|
||||
#define zhHL CPU->HL.B.H
|
||||
#define zH zhHL
|
||||
#define zL zlHL
|
||||
|
||||
#define zAF2 CPU->AF2.W
|
||||
#define zlAF2 CPU->AF2.B.L
|
||||
#define zhAF2 CPU->AF2.B.H
|
||||
#define zA2 zhAF2
|
||||
#define zF2 zlAF2
|
||||
|
||||
#define zBC2 CPU->BC2.W
|
||||
#define zDE2 CPU->DE2.W
|
||||
#define zHL2 CPU->HL2.W
|
||||
|
||||
#define pzIX &(CPU->IX)
|
||||
#define zIX CPU->IX.W
|
||||
#define zlIX CPU->IX.B.L
|
||||
#define zhIX CPU->IX.B.H
|
||||
|
||||
#define pzIY &(CPU->IY)
|
||||
#define zIY CPU->IY.W
|
||||
#define zlIY CPU->IY.B.L
|
||||
#define zhIY CPU->IY.B.H
|
||||
|
||||
#define pzSP &(CPU->SP)
|
||||
#define zSP CPU->SP.W
|
||||
#define zlSP CPU->SP.B.L
|
||||
#define zhSP CPU->SP.B.H
|
||||
|
||||
#define zRealPC (PC - CPU->BasePC)
|
||||
#define zPC PC
|
||||
|
||||
#define zI CPU->I
|
||||
#define zIM CPU->IM
|
||||
|
||||
#define zwR CPU->R.W
|
||||
#define zR1 CPU->R.B.L
|
||||
#define zR2 CPU->R.B.H
|
||||
#define zR zR1
|
||||
|
||||
#define zIFF CPU->IFF.W
|
||||
#define zIFF1 CPU->IFF.B.L
|
||||
#define zIFF2 CPU->IFF.B.H
|
||||
|
||||
#define CZ80_SF_SFT 7
|
||||
#define CZ80_ZF_SFT 6
|
||||
#define CZ80_YF_SFT 5
|
||||
#define CZ80_HF_SFT 4
|
||||
#define CZ80_XF_SFT 3
|
||||
#define CZ80_PF_SFT 2
|
||||
#define CZ80_VF_SFT 2
|
||||
#define CZ80_NF_SFT 1
|
||||
#define CZ80_CF_SFT 0
|
||||
|
||||
#define CZ80_SF (1 << CZ80_SF_SFT)
|
||||
#define CZ80_ZF (1 << CZ80_ZF_SFT)
|
||||
#define CZ80_YF (1 << CZ80_YF_SFT)
|
||||
#define CZ80_HF (1 << CZ80_HF_SFT)
|
||||
#define CZ80_XF (1 << CZ80_XF_SFT)
|
||||
#define CZ80_PF (1 << CZ80_PF_SFT)
|
||||
#define CZ80_VF (1 << CZ80_VF_SFT)
|
||||
#define CZ80_NF (1 << CZ80_NF_SFT)
|
||||
#define CZ80_CF (1 << CZ80_CF_SFT)
|
||||
|
||||
#define CZ80_IFF_SFT CZ80_PF_SFT
|
||||
#define CZ80_IFF CZ80_PF
|
||||
|
||||
#ifndef IRQ_LINE_STATE
|
||||
#define IRQ_LINE_STATE
|
||||
#define CLEAR_LINE 0 /* clear (a fired, held or pulsed) line */
|
||||
#define ASSERT_LINE 1 /* assert an interrupt immediately */
|
||||
#define HOLD_LINE 2 /* hold interrupt line until acknowledged */
|
||||
#define PULSE_LINE 3 /* pulse interrupt line for one instruction */
|
||||
#define IRQ_LINE_NMI 127 /* IRQ line for NMIs */
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
CZ80_PC = 1,
|
||||
CZ80_SP,
|
||||
CZ80_AF,
|
||||
CZ80_BC,
|
||||
CZ80_DE,
|
||||
CZ80_HL,
|
||||
CZ80_IX,
|
||||
CZ80_IY,
|
||||
CZ80_AF2,
|
||||
CZ80_BC2,
|
||||
CZ80_DE2,
|
||||
CZ80_HL2,
|
||||
CZ80_R,
|
||||
CZ80_I,
|
||||
CZ80_IM,
|
||||
CZ80_IFF1,
|
||||
CZ80_IFF2,
|
||||
CZ80_HALT,
|
||||
CZ80_IRQ
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
#if CZ80_LITTLE_ENDIAN
|
||||
UINT8 L;
|
||||
UINT8 H;
|
||||
#else
|
||||
UINT8 H;
|
||||
UINT8 L;
|
||||
#endif
|
||||
} B;
|
||||
UINT16 W;
|
||||
} union16;
|
||||
|
||||
typedef struct cz80_t
|
||||
{
|
||||
union
|
||||
{
|
||||
UINT8 r8[8];
|
||||
union16 r16[4];
|
||||
struct
|
||||
{
|
||||
union16 BC;
|
||||
union16 DE;
|
||||
union16 HL;
|
||||
union16 AF;
|
||||
};
|
||||
};
|
||||
|
||||
union16 IX;
|
||||
union16 IY;
|
||||
union16 SP;
|
||||
UINT32 unusedPC; /* left for binary compat */
|
||||
|
||||
union16 BC2;
|
||||
union16 DE2;
|
||||
union16 HL2;
|
||||
union16 AF2;
|
||||
|
||||
union16 R;
|
||||
union16 IFF;
|
||||
|
||||
UINT8 I;
|
||||
UINT8 IM;
|
||||
UINT8 HaltState;
|
||||
UINT8 dummy;
|
||||
|
||||
INT32 IRQLine;
|
||||
INT32 IRQState;
|
||||
INT32 ICount;
|
||||
INT32 ExtraCycles;
|
||||
|
||||
FPTR BasePC;
|
||||
FPTR PC;
|
||||
FPTR Fetch[CZ80_FETCH_BANK];
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
FPTR OPBase;
|
||||
FPTR OPFetch[CZ80_FETCH_BANK];
|
||||
#endif
|
||||
|
||||
UINT8 *pzR8[8];
|
||||
union16 *pzR16[4];
|
||||
|
||||
UINT8 (*Read_Byte)(UINT32 address);
|
||||
void (*Write_Byte)(UINT32 address, UINT8 data);
|
||||
|
||||
UINT8 (*IN_Port)(UINT16 port);
|
||||
void (*OUT_Port)(UINT16 port, UINT8 value);
|
||||
|
||||
INT32 (*Interrupt_Callback)(INT32 irqline);
|
||||
|
||||
} cz80_struc;
|
||||
|
||||
|
||||
/*************************/
|
||||
/* Publics Z80 variables */
|
||||
/*************************/
|
||||
|
||||
extern cz80_struc CZ80;
|
||||
|
||||
/*************************/
|
||||
/* Publics Z80 functions */
|
||||
/*************************/
|
||||
|
||||
void Cz80_Init(cz80_struc *CPU);
|
||||
|
||||
void Cz80_Reset(cz80_struc *CPU);
|
||||
|
||||
INT32 Cz80_Exec(cz80_struc *CPU, INT32 cycles);
|
||||
|
||||
void Cz80_Set_IRQ(cz80_struc *CPU, INT32 line, INT32 state);
|
||||
|
||||
UINT32 Cz80_Get_Reg(cz80_struc *CPU, INT32 regnum);
|
||||
void Cz80_Set_Reg(cz80_struc *CPU, INT32 regnum, UINT32 value);
|
||||
|
||||
void Cz80_Set_Fetch(cz80_struc *CPU, UINT32 low_adr, UINT32 high_adr, FPTR fetch_adr);
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
void Cz80_Set_Encrypt_Range(cz80_struc *CPU, UINT32 low_adr, UINT32 high_adr, UINT32 decrypted_rom);
|
||||
#endif
|
||||
|
||||
void Cz80_Set_ReadB(cz80_struc *CPU, UINT8 (*Func)(UINT32 address));
|
||||
void Cz80_Set_WriteB(cz80_struc *CPU, void (*Func)(UINT32 address, UINT8 data));
|
||||
|
||||
void Cz80_Set_INPort(cz80_struc *CPU, UINT8 (*Func)(UINT16 port));
|
||||
void Cz80_Set_OUTPort(cz80_struc *CPU, void (*Func)(UINT16 port, UINT8 value));
|
||||
|
||||
void Cz80_Set_IRQ_Callback(cz80_struc *CPU, INT32 (*Func)(INT32 irqline));
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* CZ80_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,470 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 CB opcode include source file
|
||||
* CZ80 emulator version 0.9
|
||||
* Copyright 2004-2005 Stéphane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
goto *JumpTableCB[Opcode];
|
||||
#else
|
||||
switch (Opcode)
|
||||
{
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------
|
||||
RLC
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x00): // RLC B
|
||||
OPCB(0x01): // RLC C
|
||||
OPCB(0x02): // RLC D
|
||||
OPCB(0x03): // RLC E
|
||||
OPCB(0x04): // RLC H
|
||||
OPCB(0x05): // RLC L
|
||||
OPCB(0x07): // RLC A
|
||||
src = zR8(Opcode);
|
||||
res = (src << 1) | (src >> 7);
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x06): // RLC (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src << 1) | (src >> 7);
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
RRC
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x08): // RRC B
|
||||
OPCB(0x09): // RRC C
|
||||
OPCB(0x0a): // RRC D
|
||||
OPCB(0x0b): // RRC E
|
||||
OPCB(0x0c): // RRC H
|
||||
OPCB(0x0d): // RRC L
|
||||
OPCB(0x0f): // RRC A
|
||||
src = zR8(Opcode & 7);
|
||||
res = (src >> 1) | (src << 7);
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x0e): // RRC (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src >> 1) | (src << 7);
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
RL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x10): // RL B
|
||||
OPCB(0x11): // RL C
|
||||
OPCB(0x12): // RL D
|
||||
OPCB(0x13): // RL E
|
||||
OPCB(0x14): // RL H
|
||||
OPCB(0x15): // RL L
|
||||
OPCB(0x17): // RL A
|
||||
src = zR8(Opcode & 7);
|
||||
res = (src << 1) | (zF & CF);
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x16): // RL (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src << 1) | (zF & CF);
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
RR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x18): // RR B
|
||||
OPCB(0x19): // RR C
|
||||
OPCB(0x1a): // RR D
|
||||
OPCB(0x1b): // RR E
|
||||
OPCB(0x1c): // RR H
|
||||
OPCB(0x1d): // RR L
|
||||
OPCB(0x1f): // RR A
|
||||
src = zR8(Opcode & 7);
|
||||
res = (src >> 1) | (zF << 7);
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x1e): // RR (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src >> 1) | (zF << 7);
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
SLA
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x20): // SLA B
|
||||
OPCB(0x21): // SLA C
|
||||
OPCB(0x22): // SLA D
|
||||
OPCB(0x23): // SLA E
|
||||
OPCB(0x24): // SLA H
|
||||
OPCB(0x25): // SLA L
|
||||
OPCB(0x27): // SLA A
|
||||
src = zR8(Opcode & 7);
|
||||
res = src << 1;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x26): // SLA (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = src << 1;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
SRA
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x28): // SRA B
|
||||
OPCB(0x29): // SRA C
|
||||
OPCB(0x2a): // SRA D
|
||||
OPCB(0x2b): // SRA E
|
||||
OPCB(0x2c): // SRA H
|
||||
OPCB(0x2d): // SRA L
|
||||
OPCB(0x2f): // SRA A
|
||||
src = zR8(Opcode & 7);
|
||||
res = (src >> 1) | (src & 0x80);
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x2e): // SRA (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src >> 1) | (src & 0x80);
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
SLL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x30): // SLL B
|
||||
OPCB(0x31): // SLL C
|
||||
OPCB(0x32): // SLL D
|
||||
OPCB(0x33): // SLL E
|
||||
OPCB(0x34): // SLL H
|
||||
OPCB(0x35): // SLL L
|
||||
OPCB(0x37): // SLL A
|
||||
src = zR8(Opcode & 7);
|
||||
res = (src << 1) | 0x01;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x36): // SLL (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = (src << 1) | 0x01;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
SRL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x38): // SRL B
|
||||
OPCB(0x39): // SRL C
|
||||
OPCB(0x3a): // SRL D
|
||||
OPCB(0x3b): // SRL E
|
||||
OPCB(0x3c): // SRL H
|
||||
OPCB(0x3d): // SRL L
|
||||
OPCB(0x3f): // SRL A
|
||||
src = zR8(Opcode & 7);
|
||||
res = src >> 1;
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
RET(8)
|
||||
|
||||
OPCB(0x3e): // SRL (HL)
|
||||
adr = zHL;
|
||||
src = READ_MEM8(adr);
|
||||
res = src >> 1;
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
BIT
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x40): // BIT 0,B
|
||||
OPCB(0x41): // BIT 0,C
|
||||
OPCB(0x42): // BIT 0,D
|
||||
OPCB(0x43): // BIT 0,E
|
||||
OPCB(0x44): // BIT 0,H
|
||||
OPCB(0x45): // BIT 0,L
|
||||
OPCB(0x47): // BIT 0,A
|
||||
|
||||
OPCB(0x48): // BIT 1,B
|
||||
OPCB(0x49): // BIT 1,C
|
||||
OPCB(0x4a): // BIT 1,D
|
||||
OPCB(0x4b): // BIT 1,E
|
||||
OPCB(0x4c): // BIT 1,H
|
||||
OPCB(0x4d): // BIT 1,L
|
||||
OPCB(0x4f): // BIT 1,A
|
||||
|
||||
OPCB(0x50): // BIT 2,B
|
||||
OPCB(0x51): // BIT 2,C
|
||||
OPCB(0x52): // BIT 2,D
|
||||
OPCB(0x53): // BIT 2,E
|
||||
OPCB(0x54): // BIT 2,H
|
||||
OPCB(0x55): // BIT 2,L
|
||||
OPCB(0x57): // BIT 2,A
|
||||
|
||||
OPCB(0x58): // BIT 3,B
|
||||
OPCB(0x59): // BIT 3,C
|
||||
OPCB(0x5a): // BIT 3,D
|
||||
OPCB(0x5b): // BIT 3,E
|
||||
OPCB(0x5c): // BIT 3,H
|
||||
OPCB(0x5d): // BIT 3,L
|
||||
OPCB(0x5f): // BIT 3,A
|
||||
|
||||
OPCB(0x60): // BIT 4,B
|
||||
OPCB(0x61): // BIT 4,C
|
||||
OPCB(0x62): // BIT 4,D
|
||||
OPCB(0x63): // BIT 4,E
|
||||
OPCB(0x64): // BIT 4,H
|
||||
OPCB(0x65): // BIT 4,L
|
||||
OPCB(0x67): // BIT 4,A
|
||||
|
||||
OPCB(0x68): // BIT 5,B
|
||||
OPCB(0x69): // BIT 5,C
|
||||
OPCB(0x6a): // BIT 5,D
|
||||
OPCB(0x6b): // BIT 5,E
|
||||
OPCB(0x6c): // BIT 5,H
|
||||
OPCB(0x6d): // BIT 5,L
|
||||
OPCB(0x6f): // BIT 5,A
|
||||
|
||||
OPCB(0x70): // BIT 6,B
|
||||
OPCB(0x71): // BIT 6,C
|
||||
OPCB(0x72): // BIT 6,D
|
||||
OPCB(0x73): // BIT 6,E
|
||||
OPCB(0x74): // BIT 6,H
|
||||
OPCB(0x75): // BIT 6,L
|
||||
OPCB(0x77): // BIT 6,A
|
||||
|
||||
OPCB(0x78): // BIT 7,B
|
||||
OPCB(0x79): // BIT 7,C
|
||||
OPCB(0x7a): // BIT 7,D
|
||||
OPCB(0x7b): // BIT 7,E
|
||||
OPCB(0x7c): // BIT 7,H
|
||||
OPCB(0x7d): // BIT 7,L
|
||||
OPCB(0x7f): // BIT 7,A
|
||||
zF = (zF & CF) | HF | SZ_BIT[zR8(Opcode & 7) & (1 << ((Opcode >> 3) & 7))];
|
||||
RET(8)
|
||||
|
||||
OPCB(0x46): // BIT 0,(HL)
|
||||
OPCB(0x4e): // BIT 1,(HL)
|
||||
OPCB(0x56): // BIT 2,(HL)
|
||||
OPCB(0x5e): // BIT 3,(HL)
|
||||
OPCB(0x66): // BIT 4,(HL)
|
||||
OPCB(0x6e): // BIT 5,(HL)
|
||||
OPCB(0x76): // BIT 6,(HL)
|
||||
OPCB(0x7e): // BIT 7,(HL)
|
||||
src = READ_MEM8(zHL);
|
||||
zF = (zF & CF) | HF | SZ_BIT[src & (1 << ((Opcode >> 3) & 7))];
|
||||
RET(12)
|
||||
|
||||
/*-----------------------------------------
|
||||
RES
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0x80): // RES 0,B
|
||||
OPCB(0x81): // RES 0,C
|
||||
OPCB(0x82): // RES 0,D
|
||||
OPCB(0x83): // RES 0,E
|
||||
OPCB(0x84): // RES 0,H
|
||||
OPCB(0x85): // RES 0,L
|
||||
OPCB(0x87): // RES 0,A
|
||||
|
||||
OPCB(0x88): // RES 1,B
|
||||
OPCB(0x89): // RES 1,C
|
||||
OPCB(0x8a): // RES 1,D
|
||||
OPCB(0x8b): // RES 1,E
|
||||
OPCB(0x8c): // RES 1,H
|
||||
OPCB(0x8d): // RES 1,L
|
||||
OPCB(0x8f): // RES 1,A
|
||||
|
||||
OPCB(0x90): // RES 2,B
|
||||
OPCB(0x91): // RES 2,C
|
||||
OPCB(0x92): // RES 2,D
|
||||
OPCB(0x93): // RES 2,E
|
||||
OPCB(0x94): // RES 2,H
|
||||
OPCB(0x95): // RES 2,L
|
||||
OPCB(0x97): // RES 2,A
|
||||
|
||||
OPCB(0x98): // RES 3,B
|
||||
OPCB(0x99): // RES 3,C
|
||||
OPCB(0x9a): // RES 3,D
|
||||
OPCB(0x9b): // RES 3,E
|
||||
OPCB(0x9c): // RES 3,H
|
||||
OPCB(0x9d): // RES 3,L
|
||||
OPCB(0x9f): // RES 3,A
|
||||
|
||||
OPCB(0xa0): // RES 4,B
|
||||
OPCB(0xa1): // RES 4,C
|
||||
OPCB(0xa2): // RES 4,D
|
||||
OPCB(0xa3): // RES 4,E
|
||||
OPCB(0xa4): // RES 4,H
|
||||
OPCB(0xa5): // RES 4,L
|
||||
OPCB(0xa7): // RES 4,A
|
||||
|
||||
OPCB(0xa8): // RES 5,B
|
||||
OPCB(0xa9): // RES 5,C
|
||||
OPCB(0xaa): // RES 5,D
|
||||
OPCB(0xab): // RES 5,E
|
||||
OPCB(0xac): // RES 5,H
|
||||
OPCB(0xad): // RES 5,L
|
||||
OPCB(0xaf): // RES 5,A
|
||||
|
||||
OPCB(0xb0): // RES 6,B
|
||||
OPCB(0xb1): // RES 6,C
|
||||
OPCB(0xb2): // RES 6,D
|
||||
OPCB(0xb3): // RES 6,E
|
||||
OPCB(0xb4): // RES 6,H
|
||||
OPCB(0xb5): // RES 6,L
|
||||
OPCB(0xb7): // RES 6,A
|
||||
|
||||
OPCB(0xb8): // RES 7,B
|
||||
OPCB(0xb9): // RES 7,C
|
||||
OPCB(0xba): // RES 7,D
|
||||
OPCB(0xbb): // RES 7,E
|
||||
OPCB(0xbc): // RES 7,H
|
||||
OPCB(0xbd): // RES 7,L
|
||||
OPCB(0xbf): // RES 7,A
|
||||
zR8(Opcode & 7) &= ~(1 << ((Opcode >> 3) & 7));
|
||||
RET(8)
|
||||
|
||||
OPCB(0x86): // RES 0,(HL)
|
||||
OPCB(0x8e): // RES 1,(HL)
|
||||
OPCB(0x96): // RES 2,(HL)
|
||||
OPCB(0x9e): // RES 3,(HL)
|
||||
OPCB(0xa6): // RES 4,(HL)
|
||||
OPCB(0xae): // RES 5,(HL)
|
||||
OPCB(0xb6): // RES 6,(HL)
|
||||
OPCB(0xbe): // RES 7,(HL)
|
||||
adr = zHL;
|
||||
res = READ_MEM8(adr);
|
||||
res &= ~(1 << ((Opcode >> 3) & 7));
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
SET
|
||||
-----------------------------------------*/
|
||||
|
||||
OPCB(0xc0): // SET 0,B
|
||||
OPCB(0xc1): // SET 0,C
|
||||
OPCB(0xc2): // SET 0,D
|
||||
OPCB(0xc3): // SET 0,E
|
||||
OPCB(0xc4): // SET 0,H
|
||||
OPCB(0xc5): // SET 0,L
|
||||
OPCB(0xc7): // SET 0,A
|
||||
|
||||
OPCB(0xc8): // SET 1,B
|
||||
OPCB(0xc9): // SET 1,C
|
||||
OPCB(0xca): // SET 1,D
|
||||
OPCB(0xcb): // SET 1,E
|
||||
OPCB(0xcc): // SET 1,H
|
||||
OPCB(0xcd): // SET 1,L
|
||||
OPCB(0xcf): // SET 1,A
|
||||
|
||||
OPCB(0xd0): // SET 2,B
|
||||
OPCB(0xd1): // SET 2,C
|
||||
OPCB(0xd2): // SET 2,D
|
||||
OPCB(0xd3): // SET 2,E
|
||||
OPCB(0xd4): // SET 2,H
|
||||
OPCB(0xd5): // SET 2,L
|
||||
OPCB(0xd7): // SET 2,A
|
||||
|
||||
OPCB(0xd8): // SET 3,B
|
||||
OPCB(0xd9): // SET 3,C
|
||||
OPCB(0xda): // SET 3,D
|
||||
OPCB(0xdb): // SET 3,E
|
||||
OPCB(0xdc): // SET 3,H
|
||||
OPCB(0xdd): // SET 3,L
|
||||
OPCB(0xdf): // SET 3,A
|
||||
|
||||
OPCB(0xe0): // SET 4,B
|
||||
OPCB(0xe1): // SET 4,C
|
||||
OPCB(0xe2): // SET 4,D
|
||||
OPCB(0xe3): // SET 4,E
|
||||
OPCB(0xe4): // SET 4,H
|
||||
OPCB(0xe5): // SET 4,L
|
||||
OPCB(0xe7): // SET 4,A
|
||||
|
||||
OPCB(0xe8): // SET 5,B
|
||||
OPCB(0xe9): // SET 5,C
|
||||
OPCB(0xea): // SET 5,D
|
||||
OPCB(0xeb): // SET 5,E
|
||||
OPCB(0xec): // SET 5,H
|
||||
OPCB(0xed): // SET 5,L
|
||||
OPCB(0xef): // SET 5,A
|
||||
|
||||
OPCB(0xf0): // SET 6,B
|
||||
OPCB(0xf1): // SET 6,C
|
||||
OPCB(0xf2): // SET 6,D
|
||||
OPCB(0xf3): // SET 6,E
|
||||
OPCB(0xf4): // SET 6,H
|
||||
OPCB(0xf5): // SET 6,L
|
||||
OPCB(0xf7): // SET 6,A
|
||||
|
||||
OPCB(0xf8): // SET 7,B
|
||||
OPCB(0xf9): // SET 7,C
|
||||
OPCB(0xfa): // SET 7,D
|
||||
OPCB(0xfb): // SET 7,E
|
||||
OPCB(0xfc): // SET 7,H
|
||||
OPCB(0xfd): // SET 7,L
|
||||
OPCB(0xff): // SET 7,A
|
||||
zR8(Opcode & 7) |= 1 << ((Opcode >> 3) & 7);
|
||||
RET(8)
|
||||
|
||||
OPCB(0xc6): // SET 0,(HL)
|
||||
OPCB(0xce): // SET 1,(HL)
|
||||
OPCB(0xd6): // SET 2,(HL)
|
||||
OPCB(0xde): // SET 3,(HL)
|
||||
OPCB(0xe6): // SET 4,(HL)
|
||||
OPCB(0xee): // SET 5,(HL)
|
||||
OPCB(0xf6): // SET 6,(HL)
|
||||
OPCB(0xfe): // SET 7,(HL)
|
||||
adr = zHL;
|
||||
res = READ_MEM8(adr);
|
||||
res |= 1 << ((Opcode >> 3) & 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(15)
|
||||
|
||||
#if !CZ80_USE_JUMPTABLE
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,731 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 ED opcode include source file
|
||||
* CZ80 emulator version 0.9
|
||||
* Copyright 2004-2005 Stéphane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
goto *JumpTableED[Opcode];
|
||||
#else
|
||||
switch (Opcode)
|
||||
{
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------
|
||||
ILLEGAL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x00):
|
||||
OPED(0x01):
|
||||
OPED(0x02):
|
||||
OPED(0x03):
|
||||
OPED(0x04):
|
||||
OPED(0x05):
|
||||
OPED(0x06):
|
||||
OPED(0x07):
|
||||
OPED(0x08):
|
||||
OPED(0x09):
|
||||
OPED(0x0a):
|
||||
OPED(0x0b):
|
||||
OPED(0x0c):
|
||||
OPED(0x0d):
|
||||
OPED(0x0e):
|
||||
OPED(0x0f):
|
||||
|
||||
OPED(0x10):
|
||||
OPED(0x11):
|
||||
OPED(0x12):
|
||||
OPED(0x13):
|
||||
OPED(0x14):
|
||||
OPED(0x15):
|
||||
OPED(0x16):
|
||||
OPED(0x17):
|
||||
OPED(0x18):
|
||||
OPED(0x19):
|
||||
OPED(0x1a):
|
||||
OPED(0x1b):
|
||||
OPED(0x1c):
|
||||
OPED(0x1d):
|
||||
OPED(0x1e):
|
||||
OPED(0x1f):
|
||||
|
||||
OPED(0x20):
|
||||
OPED(0x21):
|
||||
OPED(0x22):
|
||||
OPED(0x23):
|
||||
OPED(0x24):
|
||||
OPED(0x25):
|
||||
OPED(0x26):
|
||||
OPED(0x27):
|
||||
OPED(0x28):
|
||||
OPED(0x29):
|
||||
OPED(0x2a):
|
||||
OPED(0x2b):
|
||||
OPED(0x2c):
|
||||
OPED(0x2d):
|
||||
OPED(0x2e):
|
||||
OPED(0x2f):
|
||||
|
||||
OPED(0x30):
|
||||
OPED(0x31):
|
||||
OPED(0x32):
|
||||
OPED(0x33):
|
||||
OPED(0x34):
|
||||
OPED(0x35):
|
||||
OPED(0x36):
|
||||
OPED(0x37):
|
||||
OPED(0x38):
|
||||
OPED(0x39):
|
||||
OPED(0x3a):
|
||||
OPED(0x3b):
|
||||
OPED(0x3c):
|
||||
OPED(0x3d):
|
||||
OPED(0x3e):
|
||||
OPED(0x3f):
|
||||
|
||||
OPED(0x77):
|
||||
OPED(0x7f):
|
||||
|
||||
OPED(0x80):
|
||||
OPED(0x81):
|
||||
OPED(0x82):
|
||||
OPED(0x83):
|
||||
OPED(0x84):
|
||||
OPED(0x85):
|
||||
OPED(0x86):
|
||||
OPED(0x87):
|
||||
OPED(0x88):
|
||||
OPED(0x89):
|
||||
OPED(0x8a):
|
||||
OPED(0x8b):
|
||||
OPED(0x8c):
|
||||
OPED(0x8d):
|
||||
OPED(0x8e):
|
||||
OPED(0x8f):
|
||||
|
||||
OPED(0x90):
|
||||
OPED(0x91):
|
||||
OPED(0x92):
|
||||
OPED(0x93):
|
||||
OPED(0x94):
|
||||
OPED(0x95):
|
||||
OPED(0x96):
|
||||
OPED(0x97):
|
||||
OPED(0x98):
|
||||
OPED(0x99):
|
||||
OPED(0x9a):
|
||||
OPED(0x9b):
|
||||
OPED(0x9c):
|
||||
OPED(0x9d):
|
||||
OPED(0x9e):
|
||||
OPED(0x9f):
|
||||
|
||||
OPED(0xa4):
|
||||
OPED(0xa5):
|
||||
OPED(0xa6):
|
||||
OPED(0xa7):
|
||||
|
||||
OPED(0xac):
|
||||
OPED(0xad):
|
||||
OPED(0xae):
|
||||
OPED(0xaf):
|
||||
|
||||
OPED(0xb4):
|
||||
OPED(0xb5):
|
||||
OPED(0xb6):
|
||||
OPED(0xb7):
|
||||
|
||||
OPED(0xbc):
|
||||
OPED(0xbd):
|
||||
OPED(0xbe):
|
||||
OPED(0xbf):
|
||||
|
||||
OPED(0xc0):
|
||||
OPED(0xc1):
|
||||
OPED(0xc2):
|
||||
OPED(0xc3):
|
||||
OPED(0xc4):
|
||||
OPED(0xc5):
|
||||
OPED(0xc6):
|
||||
OPED(0xc7):
|
||||
OPED(0xc8):
|
||||
OPED(0xc9):
|
||||
OPED(0xca):
|
||||
OPED(0xcb):
|
||||
OPED(0xcc):
|
||||
OPED(0xcd):
|
||||
OPED(0xce):
|
||||
OPED(0xcf):
|
||||
|
||||
OPED(0xd0):
|
||||
OPED(0xd1):
|
||||
OPED(0xd2):
|
||||
OPED(0xd3):
|
||||
OPED(0xd4):
|
||||
OPED(0xd5):
|
||||
OPED(0xd6):
|
||||
OPED(0xd7):
|
||||
OPED(0xd8):
|
||||
OPED(0xd9):
|
||||
OPED(0xda):
|
||||
OPED(0xdb):
|
||||
OPED(0xdc):
|
||||
OPED(0xdd):
|
||||
OPED(0xde):
|
||||
OPED(0xdf):
|
||||
|
||||
OPED(0xe0):
|
||||
OPED(0xe1):
|
||||
OPED(0xe2):
|
||||
OPED(0xe3):
|
||||
OPED(0xe4):
|
||||
OPED(0xe5):
|
||||
OPED(0xe6):
|
||||
OPED(0xe7):
|
||||
OPED(0xe8):
|
||||
OPED(0xe9):
|
||||
OPED(0xea):
|
||||
OPED(0xeb):
|
||||
OPED(0xec):
|
||||
OPED(0xed):
|
||||
OPED(0xee):
|
||||
OPED(0xef):
|
||||
|
||||
OPED(0xf0):
|
||||
OPED(0xf1):
|
||||
OPED(0xf2):
|
||||
OPED(0xf3):
|
||||
OPED(0xf4):
|
||||
OPED(0xf5):
|
||||
OPED(0xf6):
|
||||
OPED(0xf7):
|
||||
OPED(0xf8):
|
||||
OPED(0xf9):
|
||||
OPED(0xfa):
|
||||
OPED(0xfb):
|
||||
OPED(0xfc):
|
||||
OPED(0xfd):
|
||||
OPED(0xfe):
|
||||
OPED(0xff):
|
||||
RET(4)
|
||||
|
||||
/*-----------------------------------------
|
||||
LD r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x47): // LD I,A
|
||||
zI = zA;
|
||||
RET(5)
|
||||
|
||||
OPED(0x4f): // LD R,A
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR = zA;
|
||||
#else
|
||||
zR = zA - ((cycles - CPU->ICount) >> 2);
|
||||
#endif
|
||||
zR2 = zA & 0x80;
|
||||
RET(5)
|
||||
|
||||
OPED(0x57): // LD A,I
|
||||
zA = zI;
|
||||
zF = (zF & CF) | SZ[zA] | zIFF2;
|
||||
RET(5)
|
||||
|
||||
OPED(0x5f): // LD A,R
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zA = (zR & 0x7f) | zR2;
|
||||
#else
|
||||
zA = ((zR + ((cycles - CPU->ICount) >> 2)) & 0x7f) | zR2;
|
||||
#endif
|
||||
zF = (zF & CF) | SZ[zA] | zIFF2;
|
||||
RET(5)
|
||||
|
||||
/*-----------------------------------------
|
||||
LD r16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x43): // LD (w),BC
|
||||
data = pzBC;
|
||||
goto OP_LD_mNN_xx;
|
||||
|
||||
OPED(0x53): // LD (w),DE
|
||||
data = pzDE;
|
||||
goto OP_LD_mNN_xx;
|
||||
|
||||
OPED(0x63): // LD (w),HL
|
||||
data = pzHL;
|
||||
goto OP_LD_mNN_xx;
|
||||
|
||||
OPED(0x73): // LD (w),SP
|
||||
data = pzSP;
|
||||
goto OP_LD_mNN_xx;
|
||||
|
||||
OPED(0x4b): // LD BC,(w)
|
||||
data = pzBC;
|
||||
goto OP_LD_xx_mNN;
|
||||
|
||||
OPED(0x5b): // LD DE,(w)
|
||||
data = pzDE;
|
||||
goto OP_LD_xx_mNN;
|
||||
|
||||
OPED(0x6b): // LD HL,(w)
|
||||
data = pzHL;
|
||||
goto OP_LD_xx_mNN;
|
||||
|
||||
OPED(0x7b): // LD SP,(w)
|
||||
data = pzSP;
|
||||
goto OP_LD_xx_mNN;
|
||||
|
||||
/*-----------------------------------------
|
||||
NEG
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x44): // NEG
|
||||
OPED(0x4c): // NEG
|
||||
OPED(0x54): // NEG
|
||||
OPED(0x5c): // NEG
|
||||
OPED(0x64): // NEG
|
||||
OPED(0x6c): // NEG
|
||||
OPED(0x74): // NEG
|
||||
OPED(0x7c): // NEG
|
||||
val = zA;
|
||||
zA = 0;
|
||||
goto OP_SUB;
|
||||
|
||||
/*-----------------------------------------
|
||||
RRD
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x67): // RRD (HL)
|
||||
adr = zHL;
|
||||
val = READ_MEM8(adr);
|
||||
WRITE_MEM8(adr, (val >> 4) | (zA << 4));
|
||||
zA = (zA & 0xf0) | (val & 0x0f);
|
||||
zF = (zF & CF) | SZP[zA];
|
||||
RET(14)
|
||||
|
||||
/*-----------------------------------------
|
||||
RLD
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x6f): // RLD (HL)
|
||||
adr = zHL;
|
||||
val = READ_MEM8(adr);
|
||||
WRITE_MEM8(adr, (val << 4) | (zA & 0x0f));
|
||||
zA = (zA & 0xf0) | (val >> 4);
|
||||
zF = (zF & CF) | SZP[zA];
|
||||
RET(14)
|
||||
|
||||
/*-----------------------------------------
|
||||
ADC 16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x7a): // ADC HL,SP
|
||||
val = zSP;
|
||||
goto OP_ADC16;
|
||||
|
||||
OPED(0x4a): // ADC HL,BC
|
||||
OPED(0x5a): // ADC HL,DE
|
||||
OPED(0x6a): // ADC HL,HL
|
||||
val = zR16((Opcode >> 4) & 3);
|
||||
|
||||
OP_ADC16:
|
||||
res = zHL + val + (zF & CF);
|
||||
zF = (((zHL ^ res ^ val) >> 8) & HF) |
|
||||
((res >> 16) & CF) |
|
||||
((res >> 8) & (SF | YF | XF)) |
|
||||
((res & 0xffff) ? 0 : ZF) |
|
||||
(((val ^ zHL ^ 0x8000) & (val ^ res) & 0x8000) >> 13);
|
||||
zHL = (UINT16)res;
|
||||
RET(11)
|
||||
|
||||
/*-----------------------------------------
|
||||
SBC 16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x72): // SBC HL,SP
|
||||
val = zSP;
|
||||
goto OP_SBC16;
|
||||
|
||||
OPED(0x42): // SBC HL,BC
|
||||
OPED(0x52): // SBC HL,DE
|
||||
OPED(0x62): // SBC HL,HL
|
||||
val = zR16((Opcode >> 4) & 3);
|
||||
|
||||
OP_SBC16:
|
||||
res = zHL - val - (zF & CF);
|
||||
zF = (((zHL ^ res ^ val) >> 8) & HF) | NF |
|
||||
((res >> 16) & CF) |
|
||||
((res >> 8) & (SF | YF | XF)) |
|
||||
((res & 0xffff) ? 0 : ZF) |
|
||||
(((val ^ zHL) & (zHL ^ res) & 0x8000) >> 13);
|
||||
zHL = (UINT16)res;
|
||||
RET(11)
|
||||
|
||||
/*-----------------------------------------
|
||||
IN
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x40): // IN B,(C)
|
||||
OPED(0x48): // IN C,(C)
|
||||
OPED(0x50): // IN D,(C)
|
||||
OPED(0x58): // IN E,(C)
|
||||
OPED(0x60): // IN H,(C)
|
||||
OPED(0x68): // IN L,(C)
|
||||
OPED(0x78): // IN E,(C)
|
||||
res = IN(zBC);
|
||||
zR8((Opcode >> 3) & 7) = res;
|
||||
zF = (zF & CF) | SZP[res];
|
||||
RET(8)
|
||||
|
||||
OPED(0x70): // IN 0,(C)
|
||||
res = IN(zBC);
|
||||
zF = (zF & CF) | SZP[res];
|
||||
RET(8)
|
||||
|
||||
/*-----------------------------------------
|
||||
OUT
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x51): // OUT (C),D
|
||||
OPED(0x41): // OUT (C),B
|
||||
OPED(0x49): // OUT (C),C
|
||||
OPED(0x59): // OUT (C),E
|
||||
OPED(0x61): // OUT (C),H
|
||||
OPED(0x69): // OUT (C),L
|
||||
OPED(0x79): // OUT (C),E
|
||||
res = zR8((Opcode >> 3) & 7);
|
||||
OUT(zBC, res);
|
||||
RET(8)
|
||||
|
||||
OPED(0x71): // OUT (C),0
|
||||
OUT(zBC, 0);
|
||||
RET(8)
|
||||
|
||||
/*-----------------------------------------
|
||||
RETN
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x45): // RETN;
|
||||
OPED(0x55): // RETN;
|
||||
OPED(0x65): // RETN;
|
||||
OPED(0x75): // RETN;
|
||||
POP_16(res);
|
||||
SET_PC(res);
|
||||
if (!zIFF1 && zIFF2)
|
||||
{
|
||||
zIFF1 = (1 << 2);
|
||||
if (CPU->IRQState)
|
||||
{
|
||||
USE_CYCLES(10)
|
||||
goto Cz80_Check_Interrupt;
|
||||
}
|
||||
}
|
||||
else zIFF1 = zIFF2;
|
||||
RET(10)
|
||||
|
||||
/*-----------------------------------------
|
||||
RETI
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x4d): // RETI
|
||||
OPED(0x5d): // RETI
|
||||
OPED(0x6d): // RETI
|
||||
OPED(0x7d): // RETI
|
||||
POP_16(res);
|
||||
SET_PC(res);
|
||||
RET(10)
|
||||
|
||||
/*-----------------------------------------
|
||||
IM
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0x46): // IM 0
|
||||
OPED(0x4e): // IM 0
|
||||
OPED(0x66): // IM 0
|
||||
OPED(0x6e): // IM 0
|
||||
zIM = 0;
|
||||
RET(4)
|
||||
|
||||
OPED(0x56): // IM 1
|
||||
OPED(0x76): // IM 1
|
||||
zIM = 1;
|
||||
RET(4)
|
||||
|
||||
OPED(0x5e): // IM 2
|
||||
OPED(0x7e): // IM 2
|
||||
zIM = 2;
|
||||
RET(4)
|
||||
|
||||
{
|
||||
UINT8 val;
|
||||
UINT8 res;
|
||||
UINT8 F;
|
||||
|
||||
/*-----------------------------------------
|
||||
LDI/LDD
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xa0): // LDI
|
||||
val = READ_MEM8(zHL++);
|
||||
WRITE_MEM8(zDE++, val);
|
||||
goto OP_LDX;
|
||||
|
||||
OPED(0xa8): // LDD
|
||||
val = READ_MEM8(zHL--);
|
||||
WRITE_MEM8(zDE--, val);
|
||||
|
||||
OP_LDX:
|
||||
F = zF & (SF | ZF | CF);
|
||||
if ((zA + val) & 0x02) F |= YF;
|
||||
if ((zA + val) & 0x08) F |= XF;
|
||||
if (--zBC) F |= VF;
|
||||
zF = F;
|
||||
RET(12)
|
||||
|
||||
/*-----------------------------------------
|
||||
LDIR/LDDR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xb0): // LDIR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL++);
|
||||
WRITE_MEM8(zDE++, val);
|
||||
zBC--;
|
||||
USE_CYCLES(17)
|
||||
} while (zBC && (CPU->ICount > 0));
|
||||
goto OP_LDXR;
|
||||
|
||||
OPED(0xb8): // LDDR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL--);
|
||||
WRITE_MEM8(zDE--, val);
|
||||
zBC--;
|
||||
USE_CYCLES(17)
|
||||
} while (zBC && (CPU->ICount > 0));
|
||||
|
||||
OP_LDXR:
|
||||
F = zF & (SF | ZF | CF);
|
||||
if ((zA + val) & 0x02) F |= YF;
|
||||
if ((zA + val) & 0x08) F |= XF;
|
||||
if (zBC)
|
||||
{
|
||||
zF = F | VF;
|
||||
PC -= 2;
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR--;
|
||||
#endif
|
||||
goto Cz80_Exec_End;
|
||||
}
|
||||
zF = F;
|
||||
ADD_CYCLES(5)
|
||||
goto Cz80_Exec;
|
||||
|
||||
/*-----------------------------------------
|
||||
CPI/CPD
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xa1): // CPI
|
||||
val = READ_MEM8(zHL++);
|
||||
goto OP_CPX;
|
||||
|
||||
OPED(0xa9): // CPD
|
||||
val = READ_MEM8(zHL--);
|
||||
|
||||
OP_CPX:
|
||||
res = zA - val;
|
||||
F = (zF & CF) | (SZ[res] & ~(YF | XF)) | ((zA ^ val ^ res) & HF) | NF;
|
||||
if (F & HF) res--;
|
||||
if (res & 0x02) F |= YF;
|
||||
if (res & 0x08) F |= XF;
|
||||
if (--zBC) F |= VF;
|
||||
zF = F;
|
||||
RET(12)
|
||||
|
||||
/*-----------------------------------------
|
||||
CPIR/CPDR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xb1): // CPIR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL++);
|
||||
res = zA - val;
|
||||
zBC--;
|
||||
F = (zF & CF) | (SZ[res] & ~(YF | XF)) | ((zA ^ val ^ res) & HF) | NF;
|
||||
if (F & HF) res--;
|
||||
if (res & 0x02) F |= YF;
|
||||
if (res & 0x08) F |= XF;
|
||||
if (zBC) F |= VF;
|
||||
zF = F;
|
||||
USE_CYCLES(17)
|
||||
} while (zBC && !(F & ZF) && (CPU->ICount > 0));
|
||||
goto OP_CPXR;
|
||||
|
||||
OPED(0xb9): // CPDR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL--);
|
||||
res = zA - val;
|
||||
zBC--;
|
||||
F = (zF & CF) | (SZ[res] & ~(YF | XF)) | ((zA ^ val ^ res) & HF) | NF;
|
||||
if (F & HF) res--;
|
||||
if (res & 0x02) F |= YF;
|
||||
if (res & 0x08) F |= XF;
|
||||
if (zBC) F |= VF;
|
||||
zF = F;
|
||||
USE_CYCLES(17)
|
||||
} while (zBC && !(F & ZF) && (CPU->ICount > 0));
|
||||
|
||||
OP_CPXR:
|
||||
if (zBC && !(F & ZF))
|
||||
{
|
||||
PC -= 2;
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR--;
|
||||
#endif
|
||||
goto Cz80_Exec_End;
|
||||
}
|
||||
ADD_CYCLES(5)
|
||||
goto Cz80_Exec;
|
||||
|
||||
/*-----------------------------------------
|
||||
INI/IND
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xa2): // INI
|
||||
val = IN(zBC);
|
||||
zB--;
|
||||
WRITE_MEM8(zHL++, val);
|
||||
goto OP_INX;
|
||||
|
||||
OPED(0xaa): // IND
|
||||
val = IN(zBC);
|
||||
zB--;
|
||||
WRITE_MEM8(zHL--, val);
|
||||
|
||||
OP_INX:
|
||||
F = SZ[zB];
|
||||
res = ((UINT32)(zC - 1) & 0xff) + (UINT32)val;
|
||||
if (val & SF) F |= NF;
|
||||
if (res & 0x100) F |= HF | CF;
|
||||
F |= SZP[(UINT8)(res & 0x07) ^ zB] & PF;
|
||||
zF = F;
|
||||
RET(12)
|
||||
|
||||
/*-----------------------------------------
|
||||
INIR/INDR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xb2): // INIR
|
||||
do
|
||||
{
|
||||
val = IN(zBC);
|
||||
zB--;
|
||||
WRITE_MEM8(zHL++, val);
|
||||
USE_CYCLES(17)
|
||||
} while (zB && (CPU->ICount > 0));
|
||||
goto OP_INXR;
|
||||
|
||||
OPED(0xba): // INDR
|
||||
do
|
||||
{
|
||||
val = IN(zBC);
|
||||
zB--;
|
||||
WRITE_MEM8(zHL--, val);
|
||||
USE_CYCLES(17)
|
||||
} while (zB && (CPU->ICount > 0));
|
||||
|
||||
OP_INXR:
|
||||
F = SZ[zB];
|
||||
res = ((UINT32)(zC - 1) & 0xff) + (UINT32)val;
|
||||
if (val & SF) F |= NF;
|
||||
if (res & 0x100) F |= HF | CF;
|
||||
F |= SZP[(UINT8)(res & 0x07) ^ zB] & PF;
|
||||
zF = F;
|
||||
if (zB)
|
||||
{
|
||||
PC -= 2;
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR--;
|
||||
#endif
|
||||
goto Cz80_Exec_End;
|
||||
}
|
||||
ADD_CYCLES(5);
|
||||
goto Cz80_Exec;
|
||||
|
||||
/*-----------------------------------------
|
||||
OUTI/OUTD
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xa3): // OUTI
|
||||
val = READ_MEM8(zHL++);
|
||||
zB--;
|
||||
OUT(zBC, val);
|
||||
goto OP_OUTX;
|
||||
|
||||
OPED(0xab): // OUTD
|
||||
val = READ_MEM8(zHL--);
|
||||
zB--;
|
||||
OUT(zBC, val);
|
||||
|
||||
OP_OUTX:
|
||||
F = SZ[zB];
|
||||
res = (UINT32)zL + (UINT32)val;
|
||||
if (val & SF) F |= NF;
|
||||
if (res & 0x100) F |= HF | CF;
|
||||
F |= SZP[(UINT8)(res & 0x07) - zB] & PF;
|
||||
zF = F;
|
||||
RET(12)
|
||||
|
||||
/*-----------------------------------------
|
||||
OTIR/OTDR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPED(0xb3): // OTIR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL++);
|
||||
zB--;
|
||||
OUT(zBC, val);
|
||||
USE_CYCLES(17)
|
||||
} while (zB && (CPU->ICount > 0));
|
||||
goto OP_OTXR;
|
||||
|
||||
OPED(0xbb): // OTDR
|
||||
do
|
||||
{
|
||||
val = READ_MEM8(zHL--);
|
||||
zB--;
|
||||
OUT(zBC, val);
|
||||
USE_CYCLES(17)
|
||||
} while (zB && (CPU->ICount > 0));
|
||||
|
||||
OP_OTXR:
|
||||
F = SZ[zB];
|
||||
res = (UINT32)zL + (UINT32)val;
|
||||
if (val & SF) F |= NF;
|
||||
if (res & 0x100) F |= HF | CF;
|
||||
F |= SZP[(UINT8)(res & 0x07) - zB] & PF;
|
||||
zF = F;
|
||||
if (zB)
|
||||
{
|
||||
PC -= 2;
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR--;
|
||||
#endif
|
||||
goto Cz80_Exec_End;
|
||||
}
|
||||
ADD_CYCLES(5)
|
||||
goto Cz80_Exec;
|
||||
}
|
||||
|
||||
#if !CZ80_USE_JUMPTABLE
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,786 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 XY opcode include source file
|
||||
* CZ80 emulator version 0.9
|
||||
* Copyright 2004-2005 St<EFBFBD>phane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
goto *JumpTableXY[Opcode];
|
||||
#else
|
||||
switch (Opcode)
|
||||
{
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------
|
||||
NOP
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x00): // NOP
|
||||
|
||||
/*-----------------------------------------
|
||||
LD r8 (same register)
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x40): // LD B,B
|
||||
OPXY(0x49): // LD C,C
|
||||
OPXY(0x52): // LD D,D
|
||||
OPXY(0x5b): // LD E,E
|
||||
OPXY(0x64): // LD H,H
|
||||
OPXY(0x6d): // LD L,L
|
||||
OPXY(0x7f): // LD A,A
|
||||
RET(4)
|
||||
|
||||
/*-----------------------------------------
|
||||
LD r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x41): // LD B,C
|
||||
OPXY(0x42): // LD B,D
|
||||
OPXY(0x43): // LD B,E
|
||||
OPXY(0x47): // LD B,A
|
||||
|
||||
OPXY(0x48): // LD C,B
|
||||
OPXY(0x4a): // LD C,D
|
||||
OPXY(0x4b): // LD C,E
|
||||
OPXY(0x4f): // LD C,A
|
||||
|
||||
OPXY(0x50): // LD D,B
|
||||
OPXY(0x51): // LD D,C
|
||||
OPXY(0x53): // LD D,E
|
||||
OPXY(0x57): // LD D,A
|
||||
|
||||
OPXY(0x58): // LD E,B
|
||||
OPXY(0x59): // LD E,C
|
||||
OPXY(0x5a): // LD E,D
|
||||
OPXY(0x5f): // LD E,A
|
||||
|
||||
OPXY(0x78): // LD A,B
|
||||
OPXY(0x79): // LD A,C
|
||||
OPXY(0x7a): // LD A,D
|
||||
OPXY(0x7b): // LD A,E
|
||||
goto OP_LD_R_R;
|
||||
|
||||
OPXY(0x44): // LD B,HX
|
||||
OPXY(0x4c): // LD C,HX
|
||||
OPXY(0x54): // LD D,HX
|
||||
OPXY(0x5c): // LD E,HX
|
||||
OPXY(0x7c): // LD A,HX
|
||||
zR8((Opcode >> 3) & 7) = data->B.H;
|
||||
RET(5)
|
||||
|
||||
OPXY(0x45): // LD B,LX
|
||||
OPXY(0x4d): // LD C,LX
|
||||
OPXY(0x55): // LD D,LX
|
||||
OPXY(0x5d): // LD E,LX
|
||||
OPXY(0x7d): // LD A,LX
|
||||
zR8((Opcode >> 3) & 7) = data->B.L;
|
||||
RET(5)
|
||||
|
||||
OPXY(0x60): // LD HX,B
|
||||
OPXY(0x61): // LD HX,C
|
||||
OPXY(0x62): // LD HX,D
|
||||
OPXY(0x63): // LD HX,E
|
||||
OPXY(0x67): // LD HX,A
|
||||
data->B.H = zR8(Opcode & 7);
|
||||
RET(5)
|
||||
|
||||
OPXY(0x68): // LD LX,B
|
||||
OPXY(0x69): // LD LX,C
|
||||
OPXY(0x6a): // LD LX,D
|
||||
OPXY(0x6b): // LD LX,E
|
||||
OPXY(0x6f): // LD LX,A
|
||||
data->B.L = zR8(Opcode & 7);
|
||||
RET(5)
|
||||
|
||||
OPXY(0x65): // LD HX,LX
|
||||
data->B.H = data->B.L;
|
||||
RET(5)
|
||||
|
||||
OPXY(0x6c): // LD LX,HX
|
||||
data->B.L = data->B.H;
|
||||
RET(5)
|
||||
|
||||
OPXY(0x06): // LD B,#imm
|
||||
OPXY(0x0e): // LD C,#imm
|
||||
OPXY(0x16): // LD D,#imm
|
||||
OPXY(0x1e): // LD E,#imm
|
||||
OPXY(0x3e): // LD A,#imm
|
||||
goto OP_LD_R_imm;
|
||||
|
||||
OPXY(0x26): // LD HX,#imm
|
||||
data->B.H = READ_ARG();
|
||||
RET(5)
|
||||
|
||||
OPXY(0x2e): // LD LX,#imm
|
||||
data->B.L = READ_ARG();
|
||||
RET(5)
|
||||
|
||||
OPXY(0x0a): // LD A,(BC)
|
||||
goto OP_LOAD_A_mBC;
|
||||
|
||||
OPXY(0x1a): // LD A,(DE)
|
||||
goto OP_LOAD_A_mDE;
|
||||
|
||||
OPXY(0x3a): // LD A,(nn)
|
||||
goto OP_LOAD_A_mNN;
|
||||
|
||||
OPXY(0x02): // LD (BC),A
|
||||
goto OP_LOAD_mBC_A;
|
||||
|
||||
OPXY(0x12): // LD (DE),A
|
||||
goto OP_LOAD_mDE_A;
|
||||
|
||||
OPXY(0x32): // LD (nn),A
|
||||
goto OP_LOAD_mNN_A;
|
||||
|
||||
OPXY(0x46): // LD B,(IX+o)
|
||||
OPXY(0x4e): // LD C,(IX+o)
|
||||
OPXY(0x56): // LD D,(IX+o)
|
||||
OPXY(0x5e): // LD E,(IX+o)
|
||||
OPXY(0x66): // LD H,(IX+o)
|
||||
OPXY(0x6e): // LD L,(IX+o)
|
||||
OPXY(0x7e): // LD A,(IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
zR8((Opcode >> 3) & 7) = READ_MEM8(adr);
|
||||
RET(15)
|
||||
|
||||
OPXY(0x70): // LD (IX+o),B
|
||||
OPXY(0x71): // LD (IX+o),C
|
||||
OPXY(0x72): // LD (IX+o),D
|
||||
OPXY(0x73): // LD (IX+o),E
|
||||
OPXY(0x74): // LD (IX+o),H
|
||||
OPXY(0x75): // LD (IX+o),L
|
||||
OPXY(0x77): // LD (IX+o),A
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
WRITE_MEM8(adr, zR8(Opcode & 7));
|
||||
RET(15)
|
||||
|
||||
OPXY(0x36): // LD (IX+o),#imm
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
WRITE_MEM8(adr, READ_ARG());
|
||||
RET(15)
|
||||
|
||||
/*-----------------------------------------
|
||||
LD r16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x01): // LD BC,nn
|
||||
OPXY(0x11): // LD DE,nn
|
||||
goto OP_LOAD_RR_imm16;
|
||||
|
||||
OPXY(0x21): // LD IX,nn
|
||||
data->W = READ_ARG16();
|
||||
RET(10)
|
||||
|
||||
OPXY(0x31): // LD SP,nn
|
||||
goto OP_LOAD_SP_imm16;
|
||||
|
||||
OPXY(0x2a): // LD IX,(w)
|
||||
goto OP_LD_xx_mNN;
|
||||
|
||||
OPXY(0x22): // LD (w),IX
|
||||
goto OP_LD_mNN_xx;
|
||||
|
||||
OPXY(0xf9): // LD SP,IX
|
||||
goto OP_LD_SP_xx;
|
||||
|
||||
/*-----------------------------------------
|
||||
POP
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xc1): // POP BC
|
||||
OPXY(0xd1): // POP DE
|
||||
OPXY(0xf1): // POP AF
|
||||
goto OP_POP_RR;
|
||||
|
||||
OPXY(0xe1): // POP IX
|
||||
goto OP_POP;
|
||||
|
||||
/*-----------------------------------------
|
||||
PUSH
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xc5): // PUSH BC
|
||||
OPXY(0xd5): // PUSH DE
|
||||
OPXY(0xf5): // PUSH AF
|
||||
goto OP_PUSH_RR;
|
||||
|
||||
OPXY(0xe5): // PUSH IX
|
||||
goto OP_PUSH;
|
||||
|
||||
/*-----------------------------------------
|
||||
EX
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x08): // EX AF,AF'
|
||||
goto OP_EX_AF_AF2;
|
||||
|
||||
OPXY(0xeb): // EX DE,HL
|
||||
goto OP_EX_DE_HL;
|
||||
|
||||
OPXY(0xd9): // EXX
|
||||
goto OP_EXX;
|
||||
|
||||
OPXY(0xe3): // EX (SP),IX
|
||||
goto OP_EX_xx_mSP;
|
||||
|
||||
/*-----------------------------------------
|
||||
INC r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x04): // INC B
|
||||
OPXY(0x0c): // INC C
|
||||
OPXY(0x14): // INC D
|
||||
OPXY(0x1c): // INC E
|
||||
OPXY(0x3c): // INC A
|
||||
goto OP_INC_R;
|
||||
|
||||
OPXY(0x24): // INC HX
|
||||
data->B.H++;
|
||||
zF = (zF & CF) | SZHV_inc[data->B.H];
|
||||
RET(5)
|
||||
|
||||
OPXY(0x2c): // INC LX
|
||||
data->B.L++;
|
||||
zF = (zF & CF) | SZHV_inc[data->B.L];
|
||||
RET(5)
|
||||
|
||||
OPXY(0x34): // INC (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
USE_CYCLES(8)
|
||||
goto OP_INC_m;
|
||||
|
||||
/*-----------------------------------------
|
||||
DEC r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x05): // DEC B
|
||||
OPXY(0x0d): // DEC C
|
||||
OPXY(0x15): // DEC D
|
||||
OPXY(0x1d): // DEC E
|
||||
OPXY(0x3d): // DEC A
|
||||
goto OP_DEC_R;
|
||||
|
||||
OPXY(0x25): // DEC HX
|
||||
data->B.H--;
|
||||
zF = (zF & CF) | SZHV_dec[data->B.H];
|
||||
RET(5)
|
||||
|
||||
OPXY(0x2d): // DEC LX
|
||||
data->B.L--;
|
||||
zF = (zF & CF) | SZHV_dec[data->B.L];
|
||||
RET(5)
|
||||
|
||||
OPXY(0x35): // DEC (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
USE_CYCLES(8)
|
||||
goto OP_DEC_m;
|
||||
|
||||
/*-----------------------------------------
|
||||
ADD r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x80): // ADD A,B
|
||||
OPXY(0x81): // ADD A,C
|
||||
OPXY(0x82): // ADD A,D
|
||||
OPXY(0x83): // ADD A,E
|
||||
OPXY(0x87): // ADD A,A
|
||||
goto OP_ADD_R;
|
||||
|
||||
OPXY(0xc6): // ADD A,n
|
||||
goto OP_ADD_imm;
|
||||
|
||||
OPXY(0x84): // ADD A,HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_ADD;
|
||||
|
||||
OPXY(0x85): // ADD A,LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_ADD;
|
||||
|
||||
OPXY(0x86): // ADD A,(IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_ADD;
|
||||
|
||||
/*-----------------------------------------
|
||||
ADC r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x88): // ADC A,B
|
||||
OPXY(0x89): // ADC A,C
|
||||
OPXY(0x8a): // ADC A,D
|
||||
OPXY(0x8b): // ADC A,E
|
||||
OPXY(0x8f): // ADC A,A
|
||||
goto OP_ADC_R;
|
||||
|
||||
OPXY(0xce): // ADC A,n
|
||||
goto OP_ADC_imm;
|
||||
|
||||
OPXY(0x8c): // ADC A,HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_ADC;
|
||||
|
||||
OPXY(0x8d): // ADC A,LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_ADC;
|
||||
|
||||
OPXY(0x8e): // ADC A,(IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_ADC;
|
||||
|
||||
/*-----------------------------------------
|
||||
SUB r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x90): // SUB B
|
||||
OPXY(0x91): // SUB C
|
||||
OPXY(0x92): // SUB D
|
||||
OPXY(0x93): // SUB E
|
||||
OPXY(0x97): // SUB A
|
||||
goto OP_SUB_R;
|
||||
|
||||
OPXY(0xd6): // SUB A,n
|
||||
goto OP_SUB_imm;
|
||||
|
||||
OPXY(0x94): // SUB HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_SUB;
|
||||
|
||||
OPXY(0x95): // SUB LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_SUB;
|
||||
|
||||
OPXY(0x96): // SUB (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_SUB;
|
||||
|
||||
/*-----------------------------------------
|
||||
SBC r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x98): // SBC A,B
|
||||
OPXY(0x99): // SBC A,C
|
||||
OPXY(0x9a): // SBC A,D
|
||||
OPXY(0x9b): // SBC A,E
|
||||
OPXY(0x9f): // SBC A,A
|
||||
goto OP_SBC_R;
|
||||
|
||||
OPXY(0xde): // SBC A,n
|
||||
goto OP_SBC_imm;
|
||||
|
||||
OPXY(0x9c): // SBC A,HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_SBC;
|
||||
|
||||
OPXY(0x9d): // SBC A,LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_SBC;
|
||||
|
||||
OPXY(0x9e): // SBC A,(IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_SBC;
|
||||
|
||||
/*-----------------------------------------
|
||||
CP r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xb8): // CP B
|
||||
OPXY(0xb9): // CP C
|
||||
OPXY(0xba): // CP D
|
||||
OPXY(0xbb): // CP E
|
||||
OPXY(0xbf): // CP A
|
||||
goto OP_CP_R;
|
||||
|
||||
OPXY(0xfe): // CP n
|
||||
goto OP_CP_imm;
|
||||
|
||||
OPXY(0xbc): // CP HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_CP;
|
||||
|
||||
OPXY(0xbd): // CP LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_CP;
|
||||
|
||||
OPXY(0xbe): // CP (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_CP;
|
||||
|
||||
/*-----------------------------------------
|
||||
AND r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xa0): // AND B
|
||||
OPXY(0xa1): // AND C
|
||||
OPXY(0xa2): // AND D
|
||||
OPXY(0xa3): // AND E
|
||||
OPXY(0xa7): // AND A
|
||||
goto OP_AND_R;
|
||||
|
||||
OPXY(0xe6): // AND A,n
|
||||
goto OP_AND_imm;
|
||||
|
||||
OPXY(0xa4): // AND HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_AND;
|
||||
|
||||
OPXY(0xa5): // AND LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_AND;
|
||||
|
||||
OPXY(0xa6): // AND (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_AND;
|
||||
|
||||
/*-----------------------------------------
|
||||
XOR r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xa8): // XOR B
|
||||
OPXY(0xa9): // XOR C
|
||||
OPXY(0xaa): // XOR D
|
||||
OPXY(0xab): // XOR E
|
||||
OPXY(0xaf): // XOR A
|
||||
goto OP_XOR_R;
|
||||
|
||||
OPXY(0xee): // XOR A,n
|
||||
goto OP_XOR_imm;
|
||||
|
||||
OPXY(0xac): // XOR HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_XOR;
|
||||
|
||||
OPXY(0xad): // XOR LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_XOR;
|
||||
|
||||
OPXY(0xae): // XOR (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_XOR;
|
||||
|
||||
/*-----------------------------------------
|
||||
OR r8
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xb0): // OR B
|
||||
OPXY(0xb1): // OR C
|
||||
OPXY(0xb2): // OR D
|
||||
OPXY(0xb3): // OR E
|
||||
OPXY(0xb7): // OR A
|
||||
goto OP_OR_R;
|
||||
|
||||
OPXY(0xf6): // OR A,n
|
||||
goto OP_OR_imm;
|
||||
|
||||
OPXY(0xb4): // OR HX
|
||||
val = data->B.H;
|
||||
USE_CYCLES(1)
|
||||
goto OP_OR;
|
||||
|
||||
OPXY(0xb5): // OR LX
|
||||
val = data->B.L;
|
||||
USE_CYCLES(1)
|
||||
goto OP_OR;
|
||||
|
||||
OPXY(0xb6): // OR (IX+o)
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
val = READ_MEM8(adr);
|
||||
USE_CYCLES(11)
|
||||
goto OP_OR;
|
||||
|
||||
/*-----------------------------------------
|
||||
MISC ARITHMETIC & CPU CONTROL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x27): // DAA
|
||||
goto OP_DAA;
|
||||
|
||||
OPXY(0x2f): // CPL
|
||||
goto OP_CPL;
|
||||
|
||||
OPXY(0x37): // SCF
|
||||
goto OP_SCF;
|
||||
|
||||
OPXY(0x3f): // CCF
|
||||
goto OP_CCF;
|
||||
|
||||
OPXY(0x76): // HALT
|
||||
goto OP_HALT;
|
||||
|
||||
OPXY(0xf3): // DI
|
||||
goto OP_DI;
|
||||
|
||||
OPXY(0xfb): // EI
|
||||
goto OP_EI;
|
||||
|
||||
/*-----------------------------------------
|
||||
INC r16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x03): // INC BC
|
||||
goto OP_INC_BC;
|
||||
|
||||
OPXY(0x13): // INC DE
|
||||
goto OP_INC_DE;
|
||||
|
||||
OPXY(0x23): // INC IX
|
||||
goto OP_INC_xx;
|
||||
|
||||
OPXY(0x33): // INC SP
|
||||
goto OP_INC_SP;
|
||||
|
||||
/*-----------------------------------------
|
||||
DEC r16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x0b): // DEC BC
|
||||
goto OP_DEC_BC;
|
||||
|
||||
OPXY(0x1b): // DEC DE
|
||||
goto OP_DEC_DE;
|
||||
|
||||
OPXY(0x2b): // DEC IX
|
||||
goto OP_DEC_xx;
|
||||
|
||||
OPXY(0x3b): // DEC SP
|
||||
goto OP_DEC_SP;
|
||||
|
||||
/*-----------------------------------------
|
||||
ADD r16
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x09): // ADD IX,BC
|
||||
goto OP_ADD16_xx_BC;
|
||||
|
||||
OPXY(0x19): // ADD IX,DE
|
||||
goto OP_ADD16_xx_DE;
|
||||
|
||||
OPXY(0x29): // ADD IX,IX
|
||||
goto OP_ADD16_xx_xx;
|
||||
|
||||
OPXY(0x39): // ADD IX,SP
|
||||
goto OP_ADD16_xx_SP;
|
||||
|
||||
/*-----------------------------------------
|
||||
ROTATE
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x07): // RLCA
|
||||
goto OP_RLCA;
|
||||
|
||||
OPXY(0x0f): // RRCA
|
||||
goto OP_RRCA;
|
||||
|
||||
OPXY(0x17): // RLA
|
||||
goto OP_RLA;
|
||||
|
||||
OPXY(0x1f): // RRA
|
||||
goto OP_RRA;
|
||||
|
||||
/*-----------------------------------------
|
||||
JP
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xc3): // JP nn
|
||||
goto OP_JP;
|
||||
|
||||
OPXY(0xe9): // JP (IX)
|
||||
goto OP_JP_xx;
|
||||
|
||||
OPXY(0xc2): // JP NZ,nn
|
||||
goto OP_JP_NZ;
|
||||
|
||||
OPXY(0xca): // JP Z,nn
|
||||
goto OP_JP_Z;
|
||||
|
||||
OPXY(0xd2): // JP NC,nn
|
||||
goto OP_JP_NC;
|
||||
|
||||
OPXY(0xda): // JP C,nn
|
||||
goto OP_JP_C;
|
||||
|
||||
OPXY(0xe2): // JP PO,nn
|
||||
goto OP_JP_PO;
|
||||
|
||||
OPXY(0xea): // JP PE,nn
|
||||
goto OP_JP_PE;
|
||||
|
||||
OPXY(0xf2): // JP P,nn
|
||||
goto OP_JP_P;
|
||||
|
||||
OPXY(0xfa): // JP M,nn
|
||||
goto OP_JP_M;
|
||||
|
||||
/*-----------------------------------------
|
||||
JR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0x10): // DJNZ n
|
||||
goto OP_DJNZ;
|
||||
|
||||
OPXY(0x18): // JR n
|
||||
goto OP_JR;
|
||||
|
||||
OPXY(0x20): // JR NZ,n
|
||||
goto OP_JR_NZ;
|
||||
|
||||
OPXY(0x28): // JR Z,n
|
||||
goto OP_JR_Z;
|
||||
|
||||
OPXY(0x30): // JR NC,n
|
||||
goto OP_JR_NC;
|
||||
|
||||
OPXY(0x38): // JR C,n
|
||||
goto OP_JR_C;
|
||||
|
||||
/*-----------------------------------------
|
||||
CALL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xcd): // CALL nn
|
||||
goto OP_CALL;
|
||||
|
||||
OPXY(0xc4): // CALL NZ,nn
|
||||
goto OP_CALL_NZ;
|
||||
|
||||
OPXY(0xcc): // CALL Z,nn
|
||||
goto OP_CALL_Z;
|
||||
|
||||
OPXY(0xd4): // CALL NC,nn
|
||||
goto OP_CALL_NC;
|
||||
|
||||
OPXY(0xdc): // CALL C,nn
|
||||
goto OP_CALL_C;
|
||||
|
||||
OPXY(0xe4): // CALL PO,nn
|
||||
goto OP_CALL_PO;
|
||||
|
||||
OPXY(0xec): // CALL PE,nn
|
||||
goto OP_CALL_PE;
|
||||
|
||||
OPXY(0xf4): // CALL P,nn
|
||||
goto OP_CALL_P;
|
||||
|
||||
OPXY(0xfc): // CALL M,nn
|
||||
goto OP_CALL_M;
|
||||
|
||||
/*-----------------------------------------
|
||||
RET
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xc9): // RET
|
||||
goto OP_RET;
|
||||
|
||||
OPXY(0xc0): // RET NZ
|
||||
goto OP_RET_NZ;
|
||||
|
||||
OPXY(0xc8): // RET Z
|
||||
goto OP_RET_Z;
|
||||
|
||||
OPXY(0xd0): // RET NC
|
||||
goto OP_RET_NC;
|
||||
|
||||
OPXY(0xd8): // RET C
|
||||
goto OP_RET_C;
|
||||
|
||||
OPXY(0xe0): // RET PO
|
||||
goto OP_RET_PO;
|
||||
|
||||
OPXY(0xe8): // RET PE
|
||||
goto OP_RET_PE;
|
||||
|
||||
OPXY(0xf0): // RET P
|
||||
goto OP_RET_P;
|
||||
|
||||
OPXY(0xf8): // RET M
|
||||
goto OP_RET_M;
|
||||
|
||||
/*-----------------------------------------
|
||||
RST
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xc7): // RST 0
|
||||
OPXY(0xcf): // RST 1
|
||||
OPXY(0xd7): // RST 2
|
||||
OPXY(0xdf): // RST 3
|
||||
OPXY(0xe7): // RST 4
|
||||
OPXY(0xef): // RST 5
|
||||
OPXY(0xf7): // RST 6
|
||||
OPXY(0xff): // RST 7
|
||||
goto OP_RST;
|
||||
|
||||
/*-----------------------------------------
|
||||
OUT
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xd3): // OUT (n),A
|
||||
goto OP_OUT_mN_A;
|
||||
|
||||
/*-----------------------------------------
|
||||
IN
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xdb): // IN A,(n)
|
||||
goto OP_IN_A_mN;
|
||||
|
||||
/*-----------------------------------------
|
||||
PREFIX
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXY(0xcb): // XYCB prefix (BIT & SHIFT INSTRUCTIONS)
|
||||
{
|
||||
UINT8 src;
|
||||
UINT8 res;
|
||||
|
||||
adr = data->W + (INT8)READ_ARG();
|
||||
Opcode = READ_ARG();
|
||||
#if CZ80_EMULATE_R_EXACTLY
|
||||
zR++;
|
||||
#endif
|
||||
#include "cz80_opXYCB.inc"
|
||||
}
|
||||
|
||||
OPXY(0xed): // ED prefix
|
||||
goto ED_PREFIX;
|
||||
|
||||
OPXY(0xdd): // DD prefix (IX)
|
||||
goto DD_PREFIX;
|
||||
|
||||
OPXY(0xfd): // FD prefix (IY)
|
||||
goto FD_PREFIX;
|
||||
|
||||
#if !CZ80_USE_JUMPTABLE
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,474 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* CZ80 XYCB opcode include source file
|
||||
* CZ80 emulator version 0.9
|
||||
* Copyright 2004-2005 Stéphane Dallongeville
|
||||
*
|
||||
* (Modified by NJ)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
goto *JumpTableXYCB[Opcode];
|
||||
#else
|
||||
switch (Opcode)
|
||||
{
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------
|
||||
RLC
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x00): // RLC (Ix+d), B
|
||||
OPXYCB(0x01): // RLC (Ix+d), C
|
||||
OPXYCB(0x02): // RLC (Ix+d), D
|
||||
OPXYCB(0x03): // RLC (Ix+d), E
|
||||
OPXYCB(0x04): // RLC (Ix+d), H
|
||||
OPXYCB(0x05): // RLC (Ix+d), L
|
||||
OPXYCB(0x07): // RLC (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | (src >> 7)) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x06): // RLC (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | (src >> 7)) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
RRC
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x08): // RRC (Ix+d), B
|
||||
OPXYCB(0x09): // RRC (Ix+d), C
|
||||
OPXYCB(0x0a): // RRC (Ix+d), D
|
||||
OPXYCB(0x0b): // RRC (Ix+d), E
|
||||
OPXYCB(0x0c): // RRC (Ix+d), H
|
||||
OPXYCB(0x0d): // RRC (Ix+d), L
|
||||
OPXYCB(0x0f): // RRC (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (src << 7)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x0e): // RRC (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (src << 7)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
RL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x10): // RL (Ix+d), B
|
||||
OPXYCB(0x11): // RL (Ix+d), C
|
||||
OPXYCB(0x12): // RL (Ix+d), D
|
||||
OPXYCB(0x13): // RL (Ix+d), E
|
||||
OPXYCB(0x14): // RL (Ix+d), H
|
||||
OPXYCB(0x15): // RL (Ix+d), L
|
||||
OPXYCB(0x17): // RL (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | (zF & CF)) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x16): // RL (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | (zF & CF)) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
RR
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x18): // RR (Ix+d), B
|
||||
OPXYCB(0x19): // RR (Ix+d), C
|
||||
OPXYCB(0x1a): // RR (Ix+d), D
|
||||
OPXYCB(0x1b): // RR (Ix+d), E
|
||||
OPXYCB(0x1c): // RR (Ix+d), H
|
||||
OPXYCB(0x1d): // RR (Ix+d), L
|
||||
OPXYCB(0x1f): // RR (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (zF << 7)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x1e): // RR (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (zF << 7)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
SLA
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x20): // SLA (Ix+d), B
|
||||
OPXYCB(0x21): // SLA (Ix+d), C
|
||||
OPXYCB(0x22): // SLA (Ix+d), D
|
||||
OPXYCB(0x23): // SLA (Ix+d), E
|
||||
OPXYCB(0x24): // SLA (Ix+d), H
|
||||
OPXYCB(0x25): // SLA (Ix+d), L
|
||||
OPXYCB(0x27): // SLA (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = (src << 1) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x26): // SLA (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = (src << 1) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
SRA
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x28): // SRA (Ix+d), B
|
||||
OPXYCB(0x29): // SRA (Ix+d), C
|
||||
OPXYCB(0x2a): // SRA (Ix+d), D
|
||||
OPXYCB(0x2b): // SRA (Ix+d), E
|
||||
OPXYCB(0x2c): // SRA (Ix+d), H
|
||||
OPXYCB(0x2d): // SRA (Ix+d), L
|
||||
OPXYCB(0x2f): // SRA (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (src & 0x80)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x2e): // SRA (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src >> 1) | (src & 0x80)) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
SLL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x30): // SLL (Ix+d), B
|
||||
OPXYCB(0x31): // SLL (Ix+d), C
|
||||
OPXYCB(0x32): // SLL (Ix+d), D
|
||||
OPXYCB(0x33): // SLL (Ix+d), E
|
||||
OPXYCB(0x34): // SLL (Ix+d), H
|
||||
OPXYCB(0x35): // SLL (Ix+d), L
|
||||
OPXYCB(0x37): // SLL (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | 0x01) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x36): // SLL (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = ((src << 1) | 0x01) & 0xff;
|
||||
zF = SZP[res] | (src >> 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
SRL
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x38): // SRL (Ix+d), B
|
||||
OPXYCB(0x39): // SRL (Ix+d), C
|
||||
OPXYCB(0x3a): // SRL (Ix+d), D
|
||||
OPXYCB(0x3b): // SRL (Ix+d), E
|
||||
OPXYCB(0x3c): // SRL (Ix+d), H
|
||||
OPXYCB(0x3d): // SRL (Ix+d), L
|
||||
OPXYCB(0x3f): // SRL (Ix+d), A
|
||||
src = READ_MEM8(adr);
|
||||
res = (src >> 1) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x3e): // SRL (Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
res = (src >> 1) & 0xff;
|
||||
zF = SZP[res] | (src & CF);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
BIT
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x40): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x41): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x42): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x43): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x44): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x45): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x47): // BIT 0,(Ix+d)
|
||||
|
||||
OPXYCB(0x48): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x49): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x4a): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x4b): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x4c): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x4d): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x4f): // BIT 1,(Ix+d)
|
||||
|
||||
OPXYCB(0x50): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x51): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x52): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x53): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x54): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x55): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x57): // BIT 2,(Ix+d)
|
||||
|
||||
OPXYCB(0x58): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x59): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x5a): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x5b): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x5c): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x5d): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x5f): // BIT 3,(Ix+d)
|
||||
|
||||
OPXYCB(0x60): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x61): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x62): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x63): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x64): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x65): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x67): // BIT 4,(Ix+d)
|
||||
|
||||
OPXYCB(0x68): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x69): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x6a): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x6b): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x6c): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x6d): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x6f): // BIT 5,(Ix+d)
|
||||
|
||||
OPXYCB(0x70): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x71): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x72): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x73): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x74): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x75): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x77): // BIT 6,(Ix+d)
|
||||
|
||||
OPXYCB(0x78): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x79): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x7a): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x7b): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x7c): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x7d): // BIT 7,(Ix+d)
|
||||
OPXYCB(0x7f): // BIT 7,(Ix+d)
|
||||
|
||||
OPXYCB(0x46): // BIT 0,(Ix+d)
|
||||
OPXYCB(0x4e): // BIT 1,(Ix+d)
|
||||
OPXYCB(0x56): // BIT 2,(Ix+d)
|
||||
OPXYCB(0x5e): // BIT 3,(Ix+d)
|
||||
OPXYCB(0x66): // BIT 4,(Ix+d)
|
||||
OPXYCB(0x6e): // BIT 5,(Ix+d)
|
||||
OPXYCB(0x76): // BIT 6,(Ix+d)
|
||||
OPXYCB(0x7e): // BIT 7,(Ix+d)
|
||||
src = READ_MEM8(adr);
|
||||
zF = (zF & CF) | HF |
|
||||
(SZ_BIT[src & (1 << ((Opcode >> 3) & 7))] & ~(YF | XF)) |
|
||||
((adr >> 8) & (YF | XF));
|
||||
RET(16)
|
||||
|
||||
/*-----------------------------------------
|
||||
RES
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0x80): // RES 0,(Ix+d),B
|
||||
OPXYCB(0x81): // RES 0,(Ix+d),C
|
||||
OPXYCB(0x82): // RES 0,(Ix+d),D
|
||||
OPXYCB(0x83): // RES 0,(Ix+d),E
|
||||
OPXYCB(0x84): // RES 0,(Ix+d),H
|
||||
OPXYCB(0x85): // RES 0,(Ix+d),L
|
||||
OPXYCB(0x87): // RES 0,(Ix+d),A
|
||||
|
||||
OPXYCB(0x88): // RES 1,(Ix+d),B
|
||||
OPXYCB(0x89): // RES 1,(Ix+d),C
|
||||
OPXYCB(0x8a): // RES 1,(Ix+d),D
|
||||
OPXYCB(0x8b): // RES 1,(Ix+d),E
|
||||
OPXYCB(0x8c): // RES 1,(Ix+d),H
|
||||
OPXYCB(0x8d): // RES 1,(Ix+d),L
|
||||
OPXYCB(0x8f): // RES 1,(Ix+d),A
|
||||
|
||||
OPXYCB(0x90): // RES 2,(Ix+d),B
|
||||
OPXYCB(0x91): // RES 2,(Ix+d),C
|
||||
OPXYCB(0x92): // RES 2,(Ix+d),D
|
||||
OPXYCB(0x93): // RES 2,(Ix+d),E
|
||||
OPXYCB(0x94): // RES 2,(Ix+d),H
|
||||
OPXYCB(0x95): // RES 2,(Ix+d),L
|
||||
OPXYCB(0x97): // RES 2,(Ix+d),A
|
||||
|
||||
OPXYCB(0x98): // RES 3,(Ix+d),B
|
||||
OPXYCB(0x99): // RES 3,(Ix+d),C
|
||||
OPXYCB(0x9a): // RES 3,(Ix+d),D
|
||||
OPXYCB(0x9b): // RES 3,(Ix+d),E
|
||||
OPXYCB(0x9c): // RES 3,(Ix+d),H
|
||||
OPXYCB(0x9d): // RES 3,(Ix+d),L
|
||||
OPXYCB(0x9f): // RES 3,(Ix+d),A
|
||||
|
||||
OPXYCB(0xa0): // RES 4,(Ix+d),B
|
||||
OPXYCB(0xa1): // RES 4,(Ix+d),C
|
||||
OPXYCB(0xa2): // RES 4,(Ix+d),D
|
||||
OPXYCB(0xa3): // RES 4,(Ix+d),E
|
||||
OPXYCB(0xa4): // RES 4,(Ix+d),H
|
||||
OPXYCB(0xa5): // RES 4,(Ix+d),L
|
||||
OPXYCB(0xa7): // RES 4,(Ix+d),A
|
||||
|
||||
OPXYCB(0xa8): // RES 5,(Ix+d),B
|
||||
OPXYCB(0xa9): // RES 5,(Ix+d),C
|
||||
OPXYCB(0xaa): // RES 5,(Ix+d),D
|
||||
OPXYCB(0xab): // RES 5,(Ix+d),E
|
||||
OPXYCB(0xac): // RES 5,(Ix+d),H
|
||||
OPXYCB(0xad): // RES 5,(Ix+d),L
|
||||
OPXYCB(0xaf): // RES 5,(Ix+d),A
|
||||
|
||||
OPXYCB(0xb0): // RES 6,(Ix+d),B
|
||||
OPXYCB(0xb1): // RES 6,(Ix+d),C
|
||||
OPXYCB(0xb2): // RES 6,(Ix+d),D
|
||||
OPXYCB(0xb3): // RES 6,(Ix+d),E
|
||||
OPXYCB(0xb4): // RES 6,(Ix+d),H
|
||||
OPXYCB(0xb5): // RES 6,(Ix+d),L
|
||||
OPXYCB(0xb7): // RES 6,(Ix+d),A
|
||||
|
||||
OPXYCB(0xb8): // RES 7,(Ix+d),B
|
||||
OPXYCB(0xb9): // RES 7,(Ix+d),C
|
||||
OPXYCB(0xba): // RES 7,(Ix+d),D
|
||||
OPXYCB(0xbb): // RES 7,(Ix+d),E
|
||||
OPXYCB(0xbc): // RES 7,(Ix+d),H
|
||||
OPXYCB(0xbd): // RES 7,(Ix+d),L
|
||||
OPXYCB(0xbf): // RES 7,(Ix+d),A
|
||||
res = READ_MEM8(adr);
|
||||
res &= ~(1 << ((Opcode >> 3) & 7));
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0x86): // RES 0,(Ix+d)
|
||||
OPXYCB(0x8e): // RES 1,(Ix+d)
|
||||
OPXYCB(0x96): // RES 2,(Ix+d)
|
||||
OPXYCB(0x9e): // RES 3,(Ix+d)
|
||||
OPXYCB(0xa6): // RES 4,(Ix+d)
|
||||
OPXYCB(0xae): // RES 5,(Ix+d)
|
||||
OPXYCB(0xb6): // RES 6,(Ix+d)
|
||||
OPXYCB(0xbe): // RES 7,(Ix+d)
|
||||
res = READ_MEM8(adr);
|
||||
res &= ~(1 << ((Opcode >> 3) & 7));
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
/*-----------------------------------------
|
||||
SET
|
||||
-----------------------------------------*/
|
||||
|
||||
OPXYCB(0xc0): // SET 0,(Ix+d),B
|
||||
OPXYCB(0xc1): // SET 0,(Ix+d),C
|
||||
OPXYCB(0xc2): // SET 0,(Ix+d),D
|
||||
OPXYCB(0xc3): // SET 0,(Ix+d),E
|
||||
OPXYCB(0xc4): // SET 0,(Ix+d),H
|
||||
OPXYCB(0xc5): // SET 0,(Ix+d),L
|
||||
OPXYCB(0xc7): // SET 0,(Ix+d),A
|
||||
|
||||
OPXYCB(0xc8): // SET 1,(Ix+d),B
|
||||
OPXYCB(0xc9): // SET 1,(Ix+d),C
|
||||
OPXYCB(0xca): // SET 1,(Ix+d),D
|
||||
OPXYCB(0xcb): // SET 1,(Ix+d),E
|
||||
OPXYCB(0xcc): // SET 1,(Ix+d),H
|
||||
OPXYCB(0xcd): // SET 1,(Ix+d),L
|
||||
OPXYCB(0xcf): // SET 1,(Ix+d),A
|
||||
|
||||
OPXYCB(0xd0): // SET 2,(Ix+d),B
|
||||
OPXYCB(0xd1): // SET 2,(Ix+d),C
|
||||
OPXYCB(0xd2): // SET 2,(Ix+d),D
|
||||
OPXYCB(0xd3): // SET 2,(Ix+d),E
|
||||
OPXYCB(0xd4): // SET 2,(Ix+d),H
|
||||
OPXYCB(0xd5): // SET 2,(Ix+d),L
|
||||
OPXYCB(0xd7): // SET 2,(Ix+d),A
|
||||
|
||||
OPXYCB(0xd8): // SET 3,(Ix+d),B
|
||||
OPXYCB(0xd9): // SET 3,(Ix+d),C
|
||||
OPXYCB(0xda): // SET 3,(Ix+d),D
|
||||
OPXYCB(0xdb): // SET 3,(Ix+d),E
|
||||
OPXYCB(0xdc): // SET 3,(Ix+d),H
|
||||
OPXYCB(0xdd): // SET 3,(Ix+d),L
|
||||
OPXYCB(0xdf): // SET 3,(Ix+d),A
|
||||
|
||||
OPXYCB(0xe0): // SET 4,(Ix+d),B
|
||||
OPXYCB(0xe1): // SET 4,(Ix+d),C
|
||||
OPXYCB(0xe2): // SET 4,(Ix+d),D
|
||||
OPXYCB(0xe3): // SET 4,(Ix+d),E
|
||||
OPXYCB(0xe4): // SET 4,(Ix+d),H
|
||||
OPXYCB(0xe5): // SET 4,(Ix+d),L
|
||||
OPXYCB(0xe7): // SET 4,(Ix+d),A
|
||||
|
||||
OPXYCB(0xe8): // SET 5,(Ix+d),B
|
||||
OPXYCB(0xe9): // SET 5,(Ix+d),C
|
||||
OPXYCB(0xea): // SET 5,(Ix+d),D
|
||||
OPXYCB(0xeb): // SET 5,(Ix+d),E
|
||||
OPXYCB(0xec): // SET 5,(Ix+d),H
|
||||
OPXYCB(0xed): // SET 5,(Ix+d),L
|
||||
OPXYCB(0xef): // SET 5,(Ix+d),A
|
||||
|
||||
OPXYCB(0xf0): // SET 6,(Ix+d),B
|
||||
OPXYCB(0xf1): // SET 6,(Ix+d),C
|
||||
OPXYCB(0xf2): // SET 6,(Ix+d),D
|
||||
OPXYCB(0xf3): // SET 6,(Ix+d),E
|
||||
OPXYCB(0xf4): // SET 6,(Ix+d),H
|
||||
OPXYCB(0xf5): // SET 6,(Ix+d),L
|
||||
OPXYCB(0xf7): // SET 6,(Ix+d),A
|
||||
|
||||
OPXYCB(0xf8): // SET 7,(Ix+d),B
|
||||
OPXYCB(0xf9): // SET 7,(Ix+d),C
|
||||
OPXYCB(0xfa): // SET 7,(Ix+d),D
|
||||
OPXYCB(0xfb): // SET 7,(Ix+d),E
|
||||
OPXYCB(0xfc): // SET 7,(Ix+d),H
|
||||
OPXYCB(0xfd): // SET 7,(Ix+d),L
|
||||
OPXYCB(0xff): // SET 7,(Ix+d),A
|
||||
res = READ_MEM8(adr);
|
||||
res |= 1 << ((Opcode >> 3) & 7);
|
||||
zR8(Opcode & 7) = res;
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
OPXYCB(0xc6): // SET 0,(Ix+d)
|
||||
OPXYCB(0xce): // SET 1,(Ix+d)
|
||||
OPXYCB(0xd6): // SET 2,(Ix+d)
|
||||
OPXYCB(0xde): // SET 3,(Ix+d)
|
||||
OPXYCB(0xe6): // SET 4,(Ix+d)
|
||||
OPXYCB(0xee): // SET 5,(Ix+d)
|
||||
OPXYCB(0xf6): // SET 6,(Ix+d)
|
||||
OPXYCB(0xfe): // SET 7,(Ix+d)
|
||||
res = READ_MEM8(adr);
|
||||
res |= 1 << ((Opcode >> 3) & 7);
|
||||
WRITE_MEM8(adr, res);
|
||||
RET(19)
|
||||
|
||||
#if !CZ80_USE_JUMPTABLE
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,422 @@
|
|||
/******************************************************************************
|
||||
|
||||
cz80jmp.c
|
||||
|
||||
CZ80 opcodeƒWƒƒƒ“ƒvƒe<EFBFBD>[ƒuƒ‹
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
static const void ALIGN_DATA *JumpTable[0x100] =
|
||||
{
|
||||
&&OP0x00, &&OP0x01, &&OP0x02, &&OP0x03,
|
||||
&&OP0x04, &&OP0x05, &&OP0x06, &&OP0x07,
|
||||
&&OP0x08, &&OP0x09, &&OP0x0a, &&OP0x0b,
|
||||
&&OP0x0c, &&OP0x0d, &&OP0x0e, &&OP0x0f,
|
||||
|
||||
&&OP0x10, &&OP0x11, &&OP0x12, &&OP0x13,
|
||||
&&OP0x14, &&OP0x15, &&OP0x16, &&OP0x17,
|
||||
&&OP0x18, &&OP0x19, &&OP0x1a, &&OP0x1b,
|
||||
&&OP0x1c, &&OP0x1d, &&OP0x1e, &&OP0x1f,
|
||||
|
||||
&&OP0x20, &&OP0x21, &&OP0x22, &&OP0x23,
|
||||
&&OP0x24, &&OP0x25, &&OP0x26, &&OP0x27,
|
||||
&&OP0x28, &&OP0x29, &&OP0x2a, &&OP0x2b,
|
||||
&&OP0x2c, &&OP0x2d, &&OP0x2e, &&OP0x2f,
|
||||
|
||||
&&OP0x30, &&OP0x31, &&OP0x32, &&OP0x33,
|
||||
&&OP0x34, &&OP0x35, &&OP0x36, &&OP0x37,
|
||||
&&OP0x38, &&OP0x39, &&OP0x3a, &&OP0x3b,
|
||||
&&OP0x3c, &&OP0x3d, &&OP0x3e, &&OP0x3f,
|
||||
|
||||
&&OP0x40, &&OP0x41, &&OP0x42, &&OP0x43,
|
||||
&&OP0x44, &&OP0x45, &&OP0x46, &&OP0x47,
|
||||
&&OP0x48, &&OP0x49, &&OP0x4a, &&OP0x4b,
|
||||
&&OP0x4c, &&OP0x4d, &&OP0x4e, &&OP0x4f,
|
||||
|
||||
&&OP0x50, &&OP0x51, &&OP0x52, &&OP0x53,
|
||||
&&OP0x54, &&OP0x55, &&OP0x56, &&OP0x57,
|
||||
&&OP0x58, &&OP0x59, &&OP0x5a, &&OP0x5b,
|
||||
&&OP0x5c, &&OP0x5d, &&OP0x5e, &&OP0x5f,
|
||||
|
||||
&&OP0x60, &&OP0x61, &&OP0x62, &&OP0x63,
|
||||
&&OP0x64, &&OP0x65, &&OP0x66, &&OP0x67,
|
||||
&&OP0x68, &&OP0x69, &&OP0x6a, &&OP0x6b,
|
||||
&&OP0x6c, &&OP0x6d, &&OP0x6e, &&OP0x6f,
|
||||
|
||||
&&OP0x70, &&OP0x71, &&OP0x72, &&OP0x73,
|
||||
&&OP0x74, &&OP0x75, &&OP0x76, &&OP0x77,
|
||||
&&OP0x78, &&OP0x79, &&OP0x7a, &&OP0x7b,
|
||||
&&OP0x7c, &&OP0x7d, &&OP0x7e, &&OP0x7f,
|
||||
|
||||
&&OP0x80, &&OP0x81, &&OP0x82, &&OP0x83,
|
||||
&&OP0x84, &&OP0x85, &&OP0x86, &&OP0x87,
|
||||
&&OP0x88, &&OP0x89, &&OP0x8a, &&OP0x8b,
|
||||
&&OP0x8c, &&OP0x8d, &&OP0x8e, &&OP0x8f,
|
||||
|
||||
&&OP0x90, &&OP0x91, &&OP0x92, &&OP0x93,
|
||||
&&OP0x94, &&OP0x95, &&OP0x96, &&OP0x97,
|
||||
&&OP0x98, &&OP0x99, &&OP0x9a, &&OP0x9b,
|
||||
&&OP0x9c, &&OP0x9d, &&OP0x9e, &&OP0x9f,
|
||||
|
||||
&&OP0xa0, &&OP0xa1, &&OP0xa2, &&OP0xa3,
|
||||
&&OP0xa4, &&OP0xa5, &&OP0xa6, &&OP0xa7,
|
||||
&&OP0xa8, &&OP0xa9, &&OP0xaa, &&OP0xab,
|
||||
&&OP0xac, &&OP0xad, &&OP0xae, &&OP0xaf,
|
||||
|
||||
&&OP0xb0, &&OP0xb1, &&OP0xb2, &&OP0xb3,
|
||||
&&OP0xb4, &&OP0xb5, &&OP0xb6, &&OP0xb7,
|
||||
&&OP0xb8, &&OP0xb9, &&OP0xba, &&OP0xbb,
|
||||
&&OP0xbc, &&OP0xbd, &&OP0xbe, &&OP0xbf,
|
||||
|
||||
&&OP0xc0, &&OP0xc1, &&OP0xc2, &&OP0xc3,
|
||||
&&OP0xc4, &&OP0xc5, &&OP0xc6, &&OP0xc7,
|
||||
&&OP0xc8, &&OP0xc9, &&OP0xca, &&OP0xcb,
|
||||
&&OP0xcc, &&OP0xcd, &&OP0xce, &&OP0xcf,
|
||||
|
||||
&&OP0xd0, &&OP0xd1, &&OP0xd2, &&OP0xd3,
|
||||
&&OP0xd4, &&OP0xd5, &&OP0xd6, &&OP0xd7,
|
||||
&&OP0xd8, &&OP0xd9, &&OP0xda, &&OP0xdb,
|
||||
&&OP0xdc, &&OP0xdd, &&OP0xde, &&OP0xdf,
|
||||
|
||||
&&OP0xe0, &&OP0xe1, &&OP0xe2, &&OP0xe3,
|
||||
&&OP0xe4, &&OP0xe5, &&OP0xe6, &&OP0xe7,
|
||||
&&OP0xe8, &&OP0xe9, &&OP0xea, &&OP0xeb,
|
||||
&&OP0xec, &&OP0xed, &&OP0xee, &&OP0xef,
|
||||
|
||||
&&OP0xf0, &&OP0xf1, &&OP0xf2, &&OP0xf3,
|
||||
&&OP0xf4, &&OP0xf5, &&OP0xf6, &&OP0xf7,
|
||||
&&OP0xf8, &&OP0xf9, &&OP0xfa, &&OP0xfb,
|
||||
&&OP0xfc, &&OP0xfd, &&OP0xfe, &&OP0xff
|
||||
};
|
||||
|
||||
static const void ALIGN_DATA *JumpTableCB[0x100] =
|
||||
{
|
||||
&&OPCB0x00, &&OPCB0x01, &&OPCB0x02, &&OPCB0x03,
|
||||
&&OPCB0x04, &&OPCB0x05, &&OPCB0x06, &&OPCB0x07,
|
||||
&&OPCB0x08, &&OPCB0x09, &&OPCB0x0a, &&OPCB0x0b,
|
||||
&&OPCB0x0c, &&OPCB0x0d, &&OPCB0x0e, &&OPCB0x0f,
|
||||
|
||||
&&OPCB0x10, &&OPCB0x11, &&OPCB0x12, &&OPCB0x13,
|
||||
&&OPCB0x14, &&OPCB0x15, &&OPCB0x16, &&OPCB0x17,
|
||||
&&OPCB0x18, &&OPCB0x19, &&OPCB0x1a, &&OPCB0x1b,
|
||||
&&OPCB0x1c, &&OPCB0x1d, &&OPCB0x1e, &&OPCB0x1f,
|
||||
|
||||
&&OPCB0x20, &&OPCB0x21, &&OPCB0x22, &&OPCB0x23,
|
||||
&&OPCB0x24, &&OPCB0x25, &&OPCB0x26, &&OPCB0x27,
|
||||
&&OPCB0x28, &&OPCB0x29, &&OPCB0x2a, &&OPCB0x2b,
|
||||
&&OPCB0x2c, &&OPCB0x2d, &&OPCB0x2e, &&OPCB0x2f,
|
||||
|
||||
&&OPCB0x30, &&OPCB0x31, &&OPCB0x32, &&OPCB0x33,
|
||||
&&OPCB0x34, &&OPCB0x35, &&OPCB0x36, &&OPCB0x37,
|
||||
&&OPCB0x38, &&OPCB0x39, &&OPCB0x3a, &&OPCB0x3b,
|
||||
&&OPCB0x3c, &&OPCB0x3d, &&OPCB0x3e, &&OPCB0x3f,
|
||||
|
||||
&&OPCB0x40, &&OPCB0x41, &&OPCB0x42, &&OPCB0x43,
|
||||
&&OPCB0x44, &&OPCB0x45, &&OPCB0x46, &&OPCB0x47,
|
||||
&&OPCB0x48, &&OPCB0x49, &&OPCB0x4a, &&OPCB0x4b,
|
||||
&&OPCB0x4c, &&OPCB0x4d, &&OPCB0x4e, &&OPCB0x4f,
|
||||
|
||||
&&OPCB0x50, &&OPCB0x51, &&OPCB0x52, &&OPCB0x53,
|
||||
&&OPCB0x54, &&OPCB0x55, &&OPCB0x56, &&OPCB0x57,
|
||||
&&OPCB0x58, &&OPCB0x59, &&OPCB0x5a, &&OPCB0x5b,
|
||||
&&OPCB0x5c, &&OPCB0x5d, &&OPCB0x5e, &&OPCB0x5f,
|
||||
|
||||
&&OPCB0x60, &&OPCB0x61, &&OPCB0x62, &&OPCB0x63,
|
||||
&&OPCB0x64, &&OPCB0x65, &&OPCB0x66, &&OPCB0x67,
|
||||
&&OPCB0x68, &&OPCB0x69, &&OPCB0x6a, &&OPCB0x6b,
|
||||
&&OPCB0x6c, &&OPCB0x6d, &&OPCB0x6e, &&OPCB0x6f,
|
||||
|
||||
&&OPCB0x70, &&OPCB0x71, &&OPCB0x72, &&OPCB0x73,
|
||||
&&OPCB0x74, &&OPCB0x75, &&OPCB0x76, &&OPCB0x77,
|
||||
&&OPCB0x78, &&OPCB0x79, &&OPCB0x7a, &&OPCB0x7b,
|
||||
&&OPCB0x7c, &&OPCB0x7d, &&OPCB0x7e, &&OPCB0x7f,
|
||||
|
||||
&&OPCB0x80, &&OPCB0x81, &&OPCB0x82, &&OPCB0x83,
|
||||
&&OPCB0x84, &&OPCB0x85, &&OPCB0x86, &&OPCB0x87,
|
||||
&&OPCB0x88, &&OPCB0x89, &&OPCB0x8a, &&OPCB0x8b,
|
||||
&&OPCB0x8c, &&OPCB0x8d, &&OPCB0x8e, &&OPCB0x8f,
|
||||
|
||||
&&OPCB0x90, &&OPCB0x91, &&OPCB0x92, &&OPCB0x93,
|
||||
&&OPCB0x94, &&OPCB0x95, &&OPCB0x96, &&OPCB0x97,
|
||||
&&OPCB0x98, &&OPCB0x99, &&OPCB0x9a, &&OPCB0x9b,
|
||||
&&OPCB0x9c, &&OPCB0x9d, &&OPCB0x9e, &&OPCB0x9f,
|
||||
|
||||
&&OPCB0xa0, &&OPCB0xa1, &&OPCB0xa2, &&OPCB0xa3,
|
||||
&&OPCB0xa4, &&OPCB0xa5, &&OPCB0xa6, &&OPCB0xa7,
|
||||
&&OPCB0xa8, &&OPCB0xa9, &&OPCB0xaa, &&OPCB0xab,
|
||||
&&OPCB0xac, &&OPCB0xad, &&OPCB0xae, &&OPCB0xaf,
|
||||
|
||||
&&OPCB0xb0, &&OPCB0xb1, &&OPCB0xb2, &&OPCB0xb3,
|
||||
&&OPCB0xb4, &&OPCB0xb5, &&OPCB0xb6, &&OPCB0xb7,
|
||||
&&OPCB0xb8, &&OPCB0xb9, &&OPCB0xba, &&OPCB0xbb,
|
||||
&&OPCB0xbc, &&OPCB0xbd, &&OPCB0xbe, &&OPCB0xbf,
|
||||
|
||||
&&OPCB0xc0, &&OPCB0xc1, &&OPCB0xc2, &&OPCB0xc3,
|
||||
&&OPCB0xc4, &&OPCB0xc5, &&OPCB0xc6, &&OPCB0xc7,
|
||||
&&OPCB0xc8, &&OPCB0xc9, &&OPCB0xca, &&OPCB0xcb,
|
||||
&&OPCB0xcc, &&OPCB0xcd, &&OPCB0xce, &&OPCB0xcf,
|
||||
|
||||
&&OPCB0xd0, &&OPCB0xd1, &&OPCB0xd2, &&OPCB0xd3,
|
||||
&&OPCB0xd4, &&OPCB0xd5, &&OPCB0xd6, &&OPCB0xd7,
|
||||
&&OPCB0xd8, &&OPCB0xd9, &&OPCB0xda, &&OPCB0xdb,
|
||||
&&OPCB0xdc, &&OPCB0xdd, &&OPCB0xde, &&OPCB0xdf,
|
||||
|
||||
&&OPCB0xe0, &&OPCB0xe1, &&OPCB0xe2, &&OPCB0xe3,
|
||||
&&OPCB0xe4, &&OPCB0xe5, &&OPCB0xe6, &&OPCB0xe7,
|
||||
&&OPCB0xe8, &&OPCB0xe9, &&OPCB0xea, &&OPCB0xeb,
|
||||
&&OPCB0xec, &&OPCB0xed, &&OPCB0xee, &&OPCB0xef,
|
||||
|
||||
&&OPCB0xf0, &&OPCB0xf1, &&OPCB0xf2, &&OPCB0xf3,
|
||||
&&OPCB0xf4, &&OPCB0xf5, &&OPCB0xf6, &&OPCB0xf7,
|
||||
&&OPCB0xf8, &&OPCB0xf9, &&OPCB0xfa, &&OPCB0xfb,
|
||||
&&OPCB0xfc, &&OPCB0xfd, &&OPCB0xfe, &&OPCB0xff
|
||||
};
|
||||
|
||||
static const void ALIGN_DATA *JumpTableED[0x100] =
|
||||
{
|
||||
&&OPED0x00, &&OPED0x01, &&OPED0x02, &&OPED0x03,
|
||||
&&OPED0x04, &&OPED0x05, &&OPED0x06, &&OPED0x07,
|
||||
&&OPED0x08, &&OPED0x09, &&OPED0x0a, &&OPED0x0b,
|
||||
&&OPED0x0c, &&OPED0x0d, &&OPED0x0e, &&OPED0x0f,
|
||||
|
||||
&&OPED0x10, &&OPED0x11, &&OPED0x12, &&OPED0x13,
|
||||
&&OPED0x14, &&OPED0x15, &&OPED0x16, &&OPED0x17,
|
||||
&&OPED0x18, &&OPED0x19, &&OPED0x1a, &&OPED0x1b,
|
||||
&&OPED0x1c, &&OPED0x1d, &&OPED0x1e, &&OPED0x1f,
|
||||
|
||||
&&OPED0x20, &&OPED0x21, &&OPED0x22, &&OPED0x23,
|
||||
&&OPED0x24, &&OPED0x25, &&OPED0x26, &&OPED0x27,
|
||||
&&OPED0x28, &&OPED0x29, &&OPED0x2a, &&OPED0x2b,
|
||||
&&OPED0x2c, &&OPED0x2d, &&OPED0x2e, &&OPED0x2f,
|
||||
|
||||
&&OPED0x30, &&OPED0x31, &&OPED0x32, &&OPED0x33,
|
||||
&&OPED0x34, &&OPED0x35, &&OPED0x36, &&OPED0x37,
|
||||
&&OPED0x38, &&OPED0x39, &&OPED0x3a, &&OPED0x3b,
|
||||
&&OPED0x3c, &&OPED0x3d, &&OPED0x3e, &&OPED0x3f,
|
||||
|
||||
&&OPED0x40, &&OPED0x41, &&OPED0x42, &&OPED0x43,
|
||||
&&OPED0x44, &&OPED0x45, &&OPED0x46, &&OPED0x47,
|
||||
&&OPED0x48, &&OPED0x49, &&OPED0x4a, &&OPED0x4b,
|
||||
&&OPED0x4c, &&OPED0x4d, &&OPED0x4e, &&OPED0x4f,
|
||||
|
||||
&&OPED0x50, &&OPED0x51, &&OPED0x52, &&OPED0x53,
|
||||
&&OPED0x54, &&OPED0x55, &&OPED0x56, &&OPED0x57,
|
||||
&&OPED0x58, &&OPED0x59, &&OPED0x5a, &&OPED0x5b,
|
||||
&&OPED0x5c, &&OPED0x5d, &&OPED0x5e, &&OPED0x5f,
|
||||
|
||||
&&OPED0x60, &&OPED0x61, &&OPED0x62, &&OPED0x63,
|
||||
&&OPED0x64, &&OPED0x65, &&OPED0x66, &&OPED0x67,
|
||||
&&OPED0x68, &&OPED0x69, &&OPED0x6a, &&OPED0x6b,
|
||||
&&OPED0x6c, &&OPED0x6d, &&OPED0x6e, &&OPED0x6f,
|
||||
|
||||
&&OPED0x70, &&OPED0x71, &&OPED0x72, &&OPED0x73,
|
||||
&&OPED0x74, &&OPED0x75, &&OPED0x76, &&OPED0x77,
|
||||
&&OPED0x78, &&OPED0x79, &&OPED0x7a, &&OPED0x7b,
|
||||
&&OPED0x7c, &&OPED0x7d, &&OPED0x7e, &&OPED0x7f,
|
||||
|
||||
&&OPED0x80, &&OPED0x81, &&OPED0x82, &&OPED0x83,
|
||||
&&OPED0x84, &&OPED0x85, &&OPED0x86, &&OPED0x87,
|
||||
&&OPED0x88, &&OPED0x89, &&OPED0x8a, &&OPED0x8b,
|
||||
&&OPED0x8c, &&OPED0x8d, &&OPED0x8e, &&OPED0x8f,
|
||||
|
||||
&&OPED0x90, &&OPED0x91, &&OPED0x92, &&OPED0x93,
|
||||
&&OPED0x94, &&OPED0x95, &&OPED0x96, &&OPED0x97,
|
||||
&&OPED0x98, &&OPED0x99, &&OPED0x9a, &&OPED0x9b,
|
||||
&&OPED0x9c, &&OPED0x9d, &&OPED0x9e, &&OPED0x9f,
|
||||
|
||||
&&OPED0xa0, &&OPED0xa1, &&OPED0xa2, &&OPED0xa3,
|
||||
&&OPED0xa4, &&OPED0xa5, &&OPED0xa6, &&OPED0xa7,
|
||||
&&OPED0xa8, &&OPED0xa9, &&OPED0xaa, &&OPED0xab,
|
||||
&&OPED0xac, &&OPED0xad, &&OPED0xae, &&OPED0xaf,
|
||||
|
||||
&&OPED0xb0, &&OPED0xb1, &&OPED0xb2, &&OPED0xb3,
|
||||
&&OPED0xb4, &&OPED0xb5, &&OPED0xb6, &&OPED0xb7,
|
||||
&&OPED0xb8, &&OPED0xb9, &&OPED0xba, &&OPED0xbb,
|
||||
&&OPED0xbc, &&OPED0xbd, &&OPED0xbe, &&OPED0xbf,
|
||||
|
||||
&&OPED0xc0, &&OPED0xc1, &&OPED0xc2, &&OPED0xc3,
|
||||
&&OPED0xc4, &&OPED0xc5, &&OPED0xc6, &&OPED0xc7,
|
||||
&&OPED0xc8, &&OPED0xc9, &&OPED0xca, &&OPED0xcb,
|
||||
&&OPED0xcc, &&OPED0xcd, &&OPED0xce, &&OPED0xcf,
|
||||
|
||||
&&OPED0xd0, &&OPED0xd1, &&OPED0xd2, &&OPED0xd3,
|
||||
&&OPED0xd4, &&OPED0xd5, &&OPED0xd6, &&OPED0xd7,
|
||||
&&OPED0xd8, &&OPED0xd9, &&OPED0xda, &&OPED0xdb,
|
||||
&&OPED0xdc, &&OPED0xdd, &&OPED0xde, &&OPED0xdf,
|
||||
|
||||
&&OPED0xe0, &&OPED0xe1, &&OPED0xe2, &&OPED0xe3,
|
||||
&&OPED0xe4, &&OPED0xe5, &&OPED0xe6, &&OPED0xe7,
|
||||
&&OPED0xe8, &&OPED0xe9, &&OPED0xea, &&OPED0xeb,
|
||||
&&OPED0xec, &&OPED0xed, &&OPED0xee, &&OPED0xef,
|
||||
|
||||
&&OPED0xf0, &&OPED0xf1, &&OPED0xf2, &&OPED0xf3,
|
||||
&&OPED0xf4, &&OPED0xf5, &&OPED0xf6, &&OPED0xf7,
|
||||
&&OPED0xf8, &&OPED0xf9, &&OPED0xfa, &&OPED0xfb,
|
||||
&&OPED0xfc, &&OPED0xfd, &&OPED0xfe, &&OPED0xff
|
||||
};
|
||||
|
||||
static const void ALIGN_DATA *JumpTableXY[0x100] =
|
||||
{
|
||||
&&OPXY0x00, &&OPXY0x01, &&OPXY0x02, &&OPXY0x03,
|
||||
&&OPXY0x04, &&OPXY0x05, &&OPXY0x06, &&OPXY0x07,
|
||||
&&OPXY0x08, &&OPXY0x09, &&OPXY0x0a, &&OPXY0x0b,
|
||||
&&OPXY0x0c, &&OPXY0x0d, &&OPXY0x0e, &&OPXY0x0f,
|
||||
|
||||
&&OPXY0x10, &&OPXY0x11, &&OPXY0x12, &&OPXY0x13,
|
||||
&&OPXY0x14, &&OPXY0x15, &&OPXY0x16, &&OPXY0x17,
|
||||
&&OPXY0x18, &&OPXY0x19, &&OPXY0x1a, &&OPXY0x1b,
|
||||
&&OPXY0x1c, &&OPXY0x1d, &&OPXY0x1e, &&OPXY0x1f,
|
||||
|
||||
&&OPXY0x20, &&OPXY0x21, &&OPXY0x22, &&OPXY0x23,
|
||||
&&OPXY0x24, &&OPXY0x25, &&OPXY0x26, &&OPXY0x27,
|
||||
&&OPXY0x28, &&OPXY0x29, &&OPXY0x2a, &&OPXY0x2b,
|
||||
&&OPXY0x2c, &&OPXY0x2d, &&OPXY0x2e, &&OPXY0x2f,
|
||||
|
||||
&&OPXY0x30, &&OPXY0x31, &&OPXY0x32, &&OPXY0x33,
|
||||
&&OPXY0x34, &&OPXY0x35, &&OPXY0x36, &&OPXY0x37,
|
||||
&&OPXY0x38, &&OPXY0x39, &&OPXY0x3a, &&OPXY0x3b,
|
||||
&&OPXY0x3c, &&OPXY0x3d, &&OPXY0x3e, &&OPXY0x3f,
|
||||
|
||||
&&OPXY0x40, &&OPXY0x41, &&OPXY0x42, &&OPXY0x43,
|
||||
&&OPXY0x44, &&OPXY0x45, &&OPXY0x46, &&OPXY0x47,
|
||||
&&OPXY0x48, &&OPXY0x49, &&OPXY0x4a, &&OPXY0x4b,
|
||||
&&OPXY0x4c, &&OPXY0x4d, &&OPXY0x4e, &&OPXY0x4f,
|
||||
|
||||
&&OPXY0x50, &&OPXY0x51, &&OPXY0x52, &&OPXY0x53,
|
||||
&&OPXY0x54, &&OPXY0x55, &&OPXY0x56, &&OPXY0x57,
|
||||
&&OPXY0x58, &&OPXY0x59, &&OPXY0x5a, &&OPXY0x5b,
|
||||
&&OPXY0x5c, &&OPXY0x5d, &&OPXY0x5e, &&OPXY0x5f,
|
||||
|
||||
&&OPXY0x60, &&OPXY0x61, &&OPXY0x62, &&OPXY0x63,
|
||||
&&OPXY0x64, &&OPXY0x65, &&OPXY0x66, &&OPXY0x67,
|
||||
&&OPXY0x68, &&OPXY0x69, &&OPXY0x6a, &&OPXY0x6b,
|
||||
&&OPXY0x6c, &&OPXY0x6d, &&OPXY0x6e, &&OPXY0x6f,
|
||||
|
||||
&&OPXY0x70, &&OPXY0x71, &&OPXY0x72, &&OPXY0x73,
|
||||
&&OPXY0x74, &&OPXY0x75, &&OPXY0x76, &&OPXY0x77,
|
||||
&&OPXY0x78, &&OPXY0x79, &&OPXY0x7a, &&OPXY0x7b,
|
||||
&&OPXY0x7c, &&OPXY0x7d, &&OPXY0x7e, &&OPXY0x7f,
|
||||
|
||||
&&OPXY0x80, &&OPXY0x81, &&OPXY0x82, &&OPXY0x83,
|
||||
&&OPXY0x84, &&OPXY0x85, &&OPXY0x86, &&OPXY0x87,
|
||||
&&OPXY0x88, &&OPXY0x89, &&OPXY0x8a, &&OPXY0x8b,
|
||||
&&OPXY0x8c, &&OPXY0x8d, &&OPXY0x8e, &&OPXY0x8f,
|
||||
|
||||
&&OPXY0x90, &&OPXY0x91, &&OPXY0x92, &&OPXY0x93,
|
||||
&&OPXY0x94, &&OPXY0x95, &&OPXY0x96, &&OPXY0x97,
|
||||
&&OPXY0x98, &&OPXY0x99, &&OPXY0x9a, &&OPXY0x9b,
|
||||
&&OPXY0x9c, &&OPXY0x9d, &&OPXY0x9e, &&OPXY0x9f,
|
||||
|
||||
&&OPXY0xa0, &&OPXY0xa1, &&OPXY0xa2, &&OPXY0xa3,
|
||||
&&OPXY0xa4, &&OPXY0xa5, &&OPXY0xa6, &&OPXY0xa7,
|
||||
&&OPXY0xa8, &&OPXY0xa9, &&OPXY0xaa, &&OPXY0xab,
|
||||
&&OPXY0xac, &&OPXY0xad, &&OPXY0xae, &&OPXY0xaf,
|
||||
|
||||
&&OPXY0xb0, &&OPXY0xb1, &&OPXY0xb2, &&OPXY0xb3,
|
||||
&&OPXY0xb4, &&OPXY0xb5, &&OPXY0xb6, &&OPXY0xb7,
|
||||
&&OPXY0xb8, &&OPXY0xb9, &&OPXY0xba, &&OPXY0xbb,
|
||||
&&OPXY0xbc, &&OPXY0xbd, &&OPXY0xbe, &&OPXY0xbf,
|
||||
|
||||
&&OPXY0xc0, &&OPXY0xc1, &&OPXY0xc2, &&OPXY0xc3,
|
||||
&&OPXY0xc4, &&OPXY0xc5, &&OPXY0xc6, &&OPXY0xc7,
|
||||
&&OPXY0xc8, &&OPXY0xc9, &&OPXY0xca, &&OPXY0xcb,
|
||||
&&OPXY0xcc, &&OPXY0xcd, &&OPXY0xce, &&OPXY0xcf,
|
||||
|
||||
&&OPXY0xd0, &&OPXY0xd1, &&OPXY0xd2, &&OPXY0xd3,
|
||||
&&OPXY0xd4, &&OPXY0xd5, &&OPXY0xd6, &&OPXY0xd7,
|
||||
&&OPXY0xd8, &&OPXY0xd9, &&OPXY0xda, &&OPXY0xdb,
|
||||
&&OPXY0xdc, &&OPXY0xdd, &&OPXY0xde, &&OPXY0xdf,
|
||||
|
||||
&&OPXY0xe0, &&OPXY0xe1, &&OPXY0xe2, &&OPXY0xe3,
|
||||
&&OPXY0xe4, &&OPXY0xe5, &&OPXY0xe6, &&OPXY0xe7,
|
||||
&&OPXY0xe8, &&OPXY0xe9, &&OPXY0xea, &&OPXY0xeb,
|
||||
&&OPXY0xec, &&OPXY0xed, &&OPXY0xee, &&OPXY0xef,
|
||||
|
||||
&&OPXY0xf0, &&OPXY0xf1, &&OPXY0xf2, &&OPXY0xf3,
|
||||
&&OPXY0xf4, &&OPXY0xf5, &&OPXY0xf6, &&OPXY0xf7,
|
||||
&&OPXY0xf8, &&OPXY0xf9, &&OPXY0xfa, &&OPXY0xfb,
|
||||
&&OPXY0xfc, &&OPXY0xfd, &&OPXY0xfe, &&OPXY0xff
|
||||
};
|
||||
|
||||
static const void ALIGN_DATA *JumpTableXYCB[0x100] =
|
||||
{
|
||||
&&OPXYCB0x00, &&OPXYCB0x01, &&OPXYCB0x02, &&OPXYCB0x03,
|
||||
&&OPXYCB0x04, &&OPXYCB0x05, &&OPXYCB0x06, &&OPXYCB0x07,
|
||||
&&OPXYCB0x08, &&OPXYCB0x09, &&OPXYCB0x0a, &&OPXYCB0x0b,
|
||||
&&OPXYCB0x0c, &&OPXYCB0x0d, &&OPXYCB0x0e, &&OPXYCB0x0f,
|
||||
|
||||
&&OPXYCB0x10, &&OPXYCB0x11, &&OPXYCB0x12, &&OPXYCB0x13,
|
||||
&&OPXYCB0x14, &&OPXYCB0x15, &&OPXYCB0x16, &&OPXYCB0x17,
|
||||
&&OPXYCB0x18, &&OPXYCB0x19, &&OPXYCB0x1a, &&OPXYCB0x1b,
|
||||
&&OPXYCB0x1c, &&OPXYCB0x1d, &&OPXYCB0x1e, &&OPXYCB0x1f,
|
||||
|
||||
&&OPXYCB0x20, &&OPXYCB0x21, &&OPXYCB0x22, &&OPXYCB0x23,
|
||||
&&OPXYCB0x24, &&OPXYCB0x25, &&OPXYCB0x26, &&OPXYCB0x27,
|
||||
&&OPXYCB0x28, &&OPXYCB0x29, &&OPXYCB0x2a, &&OPXYCB0x2b,
|
||||
&&OPXYCB0x2c, &&OPXYCB0x2d, &&OPXYCB0x2e, &&OPXYCB0x2f,
|
||||
|
||||
&&OPXYCB0x30, &&OPXYCB0x31, &&OPXYCB0x32, &&OPXYCB0x33,
|
||||
&&OPXYCB0x34, &&OPXYCB0x35, &&OPXYCB0x36, &&OPXYCB0x37,
|
||||
&&OPXYCB0x38, &&OPXYCB0x39, &&OPXYCB0x3a, &&OPXYCB0x3b,
|
||||
&&OPXYCB0x3c, &&OPXYCB0x3d, &&OPXYCB0x3e, &&OPXYCB0x3f,
|
||||
|
||||
&&OPXYCB0x40, &&OPXYCB0x41, &&OPXYCB0x42, &&OPXYCB0x43,
|
||||
&&OPXYCB0x44, &&OPXYCB0x45, &&OPXYCB0x46, &&OPXYCB0x47,
|
||||
&&OPXYCB0x48, &&OPXYCB0x49, &&OPXYCB0x4a, &&OPXYCB0x4b,
|
||||
&&OPXYCB0x4c, &&OPXYCB0x4d, &&OPXYCB0x4e, &&OPXYCB0x4f,
|
||||
|
||||
&&OPXYCB0x50, &&OPXYCB0x51, &&OPXYCB0x52, &&OPXYCB0x53,
|
||||
&&OPXYCB0x54, &&OPXYCB0x55, &&OPXYCB0x56, &&OPXYCB0x57,
|
||||
&&OPXYCB0x58, &&OPXYCB0x59, &&OPXYCB0x5a, &&OPXYCB0x5b,
|
||||
&&OPXYCB0x5c, &&OPXYCB0x5d, &&OPXYCB0x5e, &&OPXYCB0x5f,
|
||||
|
||||
&&OPXYCB0x60, &&OPXYCB0x61, &&OPXYCB0x62, &&OPXYCB0x63,
|
||||
&&OPXYCB0x64, &&OPXYCB0x65, &&OPXYCB0x66, &&OPXYCB0x67,
|
||||
&&OPXYCB0x68, &&OPXYCB0x69, &&OPXYCB0x6a, &&OPXYCB0x6b,
|
||||
&&OPXYCB0x6c, &&OPXYCB0x6d, &&OPXYCB0x6e, &&OPXYCB0x6f,
|
||||
|
||||
&&OPXYCB0x70, &&OPXYCB0x71, &&OPXYCB0x72, &&OPXYCB0x73,
|
||||
&&OPXYCB0x74, &&OPXYCB0x75, &&OPXYCB0x76, &&OPXYCB0x77,
|
||||
&&OPXYCB0x78, &&OPXYCB0x79, &&OPXYCB0x7a, &&OPXYCB0x7b,
|
||||
&&OPXYCB0x7c, &&OPXYCB0x7d, &&OPXYCB0x7e, &&OPXYCB0x7f,
|
||||
|
||||
&&OPXYCB0x80, &&OPXYCB0x81, &&OPXYCB0x82, &&OPXYCB0x83,
|
||||
&&OPXYCB0x84, &&OPXYCB0x85, &&OPXYCB0x86, &&OPXYCB0x87,
|
||||
&&OPXYCB0x88, &&OPXYCB0x89, &&OPXYCB0x8a, &&OPXYCB0x8b,
|
||||
&&OPXYCB0x8c, &&OPXYCB0x8d, &&OPXYCB0x8e, &&OPXYCB0x8f,
|
||||
|
||||
&&OPXYCB0x90, &&OPXYCB0x91, &&OPXYCB0x92, &&OPXYCB0x93,
|
||||
&&OPXYCB0x94, &&OPXYCB0x95, &&OPXYCB0x96, &&OPXYCB0x97,
|
||||
&&OPXYCB0x98, &&OPXYCB0x99, &&OPXYCB0x9a, &&OPXYCB0x9b,
|
||||
&&OPXYCB0x9c, &&OPXYCB0x9d, &&OPXYCB0x9e, &&OPXYCB0x9f,
|
||||
|
||||
&&OPXYCB0xa0, &&OPXYCB0xa1, &&OPXYCB0xa2, &&OPXYCB0xa3,
|
||||
&&OPXYCB0xa4, &&OPXYCB0xa5, &&OPXYCB0xa6, &&OPXYCB0xa7,
|
||||
&&OPXYCB0xa8, &&OPXYCB0xa9, &&OPXYCB0xaa, &&OPXYCB0xab,
|
||||
&&OPXYCB0xac, &&OPXYCB0xad, &&OPXYCB0xae, &&OPXYCB0xaf,
|
||||
|
||||
&&OPXYCB0xb0, &&OPXYCB0xb1, &&OPXYCB0xb2, &&OPXYCB0xb3,
|
||||
&&OPXYCB0xb4, &&OPXYCB0xb5, &&OPXYCB0xb6, &&OPXYCB0xb7,
|
||||
&&OPXYCB0xb8, &&OPXYCB0xb9, &&OPXYCB0xba, &&OPXYCB0xbb,
|
||||
&&OPXYCB0xbc, &&OPXYCB0xbd, &&OPXYCB0xbe, &&OPXYCB0xbf,
|
||||
|
||||
&&OPXYCB0xc0, &&OPXYCB0xc1, &&OPXYCB0xc2, &&OPXYCB0xc3,
|
||||
&&OPXYCB0xc4, &&OPXYCB0xc5, &&OPXYCB0xc6, &&OPXYCB0xc7,
|
||||
&&OPXYCB0xc8, &&OPXYCB0xc9, &&OPXYCB0xca, &&OPXYCB0xcb,
|
||||
&&OPXYCB0xcc, &&OPXYCB0xcd, &&OPXYCB0xce, &&OPXYCB0xcf,
|
||||
|
||||
&&OPXYCB0xd0, &&OPXYCB0xd1, &&OPXYCB0xd2, &&OPXYCB0xd3,
|
||||
&&OPXYCB0xd4, &&OPXYCB0xd5, &&OPXYCB0xd6, &&OPXYCB0xd7,
|
||||
&&OPXYCB0xd8, &&OPXYCB0xd9, &&OPXYCB0xda, &&OPXYCB0xdb,
|
||||
&&OPXYCB0xdc, &&OPXYCB0xdd, &&OPXYCB0xde, &&OPXYCB0xdf,
|
||||
|
||||
&&OPXYCB0xe0, &&OPXYCB0xe1, &&OPXYCB0xe2, &&OPXYCB0xe3,
|
||||
&&OPXYCB0xe4, &&OPXYCB0xe5, &&OPXYCB0xe6, &&OPXYCB0xe7,
|
||||
&&OPXYCB0xe8, &&OPXYCB0xe9, &&OPXYCB0xea, &&OPXYCB0xeb,
|
||||
&&OPXYCB0xec, &&OPXYCB0xed, &&OPXYCB0xee, &&OPXYCB0xef,
|
||||
|
||||
&&OPXYCB0xf0, &&OPXYCB0xf1, &&OPXYCB0xf2, &&OPXYCB0xf3,
|
||||
&&OPXYCB0xf4, &&OPXYCB0xf5, &&OPXYCB0xf6, &&OPXYCB0xf7,
|
||||
&&OPXYCB0xf8, &&OPXYCB0xf9, &&OPXYCB0xfa, &&OPXYCB0xfb,
|
||||
&&OPXYCB0xfc, &&OPXYCB0xfd, &&OPXYCB0xfe, &&OPXYCB0xff
|
||||
};
|
|
@ -0,0 +1,129 @@
|
|||
/******************************************************************************
|
||||
|
||||
cz80macro.h
|
||||
|
||||
CZ80 ŠeŽíƒ}ƒNƒ<EFBFBD>
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#if CZ80_USE_JUMPTABLE
|
||||
#define _SSOP(A,B) A##B
|
||||
#define OP(A) _SSOP(OP,A)
|
||||
#define OPCB(A) _SSOP(OPCB,A)
|
||||
#define OPED(A) _SSOP(OPED,A)
|
||||
#define OPXY(A) _SSOP(OPXY,A)
|
||||
#define OPXYCB(A) _SSOP(OPXYCB,A)
|
||||
#else
|
||||
#define OP(A) case A
|
||||
#define OPCB(A) case A
|
||||
#define OPED(A) case A
|
||||
#define OPXY(A) case A
|
||||
#define OPXYCB(A) case A
|
||||
#endif
|
||||
|
||||
#define USE_CYCLES(A) CPU->ICount -= (A);
|
||||
#define ADD_CYCLES(A) CPU->ICount += (A);
|
||||
|
||||
#define RET(A) { USE_CYCLES(A) goto Cz80_Exec; }
|
||||
|
||||
#if CZ80_ENCRYPTED_ROM
|
||||
|
||||
#define SET_PC(A) \
|
||||
CPU->BasePC = CPU->Fetch[(A) >> CZ80_FETCH_SFT]; \
|
||||
OPBase = CPU->OPFetch[(A) >> CZ80_FETCH_SFT]; \
|
||||
PC = (A) + CPU->BasePC;
|
||||
|
||||
#define GET_OP() (*(UINT8 *)(OPBase + PC))
|
||||
|
||||
#else
|
||||
|
||||
#define SET_PC(A) \
|
||||
CPU->BasePC = CPU->Fetch[(A) >> CZ80_FETCH_SFT]; \
|
||||
PC = (A) + CPU->BasePC;
|
||||
|
||||
#define GET_OP() (*(UINT8 *)PC)
|
||||
|
||||
#endif
|
||||
|
||||
#define READ_OP() GET_OP(); PC++
|
||||
|
||||
#define READ_ARG() (*(UINT8 *)PC++)
|
||||
#if CZ80_LITTLE_ENDIAN
|
||||
#define READ_ARG16() (*(UINT8 *)PC | (*(UINT8 *)(PC + 1) << 8)); PC += 2
|
||||
#else
|
||||
#define READ_ARG16() (*(UINT8 *)(PC + 1) | (*(UINT8 *)PC << 8)); PC += 2
|
||||
#endif
|
||||
|
||||
//#ifndef BUILD_CPS1PSP
|
||||
//#define READ_MEM8(A) memory_region_cpu2[(A)]
|
||||
//#else
|
||||
#if PICODRIVE_HACKS
|
||||
#define READ_MEM8(A) picodrive_read(A)
|
||||
#else
|
||||
#define READ_MEM8(A) CPU->Read_Byte(A)
|
||||
#endif
|
||||
//#endif
|
||||
#if CZ80_LITTLE_ENDIAN
|
||||
#define READ_MEM16(A) (READ_MEM8(A) | (READ_MEM8((A) + 1) << 8))
|
||||
#else
|
||||
#define READ_MEM16(A) ((READ_MEM8(A) << 8) | READ_MEM8((A) + 1))
|
||||
#endif
|
||||
|
||||
#if PICODRIVE_HACKS
|
||||
#define WRITE_MEM8(A, D) { \
|
||||
unsigned short a = A; \
|
||||
unsigned char d = D; \
|
||||
unsigned long v = z80_write_map[a >> Z80_MEM_SHIFT]; \
|
||||
if (map_flag_set(v)) \
|
||||
((z80_write_f *)(v << 1))(a, d); \
|
||||
else \
|
||||
*(unsigned char *)((v << 1) + a) = d; \
|
||||
}
|
||||
#else
|
||||
#define WRITE_MEM8(A, D) CPU->Write_Byte(A, D);
|
||||
#endif
|
||||
#if CZ80_LITTLE_ENDIAN
|
||||
#define WRITE_MEM16(A, D) { WRITE_MEM8(A, D); WRITE_MEM8((A) + 1, (D) >> 8); }
|
||||
#else
|
||||
#define WRITE_MEM16(A, D) { WRITE_MEM8((A) + 1, D); WRITE_MEM8(A, (D) >> 8); }
|
||||
#endif
|
||||
|
||||
#define PUSH_16(A) { UINT32 sp; zSP -= 2; sp = zSP; WRITE_MEM16(sp, A); }
|
||||
#define POP_16(A) { UINT32 sp; sp = zSP; A = READ_MEM16(sp); zSP = sp + 2; }
|
||||
|
||||
#define IN(A) CPU->IN_Port(A)
|
||||
#define OUT(A, D) CPU->OUT_Port(A, D)
|
||||
|
||||
#define CHECK_INT \
|
||||
if (zIFF1) \
|
||||
{ \
|
||||
UINT32 IntVect; \
|
||||
\
|
||||
if (CPU->IRQState == HOLD_LINE) \
|
||||
CPU->IRQState = CLEAR_LINE; \
|
||||
\
|
||||
CPU->HaltState = 0; \
|
||||
zIFF1 = zIFF2 = 0; \
|
||||
IntVect = CPU->Interrupt_Callback(CPU->IRQLine); \
|
||||
\
|
||||
PUSH_16(zRealPC) \
|
||||
\
|
||||
if (zIM == 2) \
|
||||
{ \
|
||||
IntVect = (IntVect & 0xff) | (zI << 8); \
|
||||
PC = READ_MEM16(IntVect); \
|
||||
CPU->ExtraCycles += 17; \
|
||||
} \
|
||||
else if (zIM == 1) \
|
||||
{ \
|
||||
PC = 0x38; \
|
||||
CPU->ExtraCycles += 13; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PC = IntVect & 0x38; \
|
||||
CPU->ExtraCycles += 13; \
|
||||
} \
|
||||
\
|
||||
SET_PC(PC) \
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*****************************************************************************/
|
||||
/* FAME Fast and Accurate Motorola 68000 Emulation Core */
|
||||
/* (c) 2005 Oscar Orallo Pelaez */
|
||||
/* Version: 1.24 */
|
||||
/* Date: 08-20-2005 */
|
||||
/* See FAME.HTML for documentation and license information */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __FAME_H__
|
||||
#define __FAME_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// PicoDrive hacks
|
||||
#define FAMEC_FETCHBITS 8
|
||||
#define M68K_FETCHBANK1 (1 << FAMEC_FETCHBITS)
|
||||
|
||||
//#define M68K_RUNNING 0x01
|
||||
#define FM68K_HALTED 0x80
|
||||
//#define M68K_WAITING 0x04
|
||||
//#define M68K_DISABLE 0x20
|
||||
//#define M68K_FAULTED 0x40
|
||||
#define FM68K_EMULATE_GROUP_0 0x02
|
||||
#define FM68K_EMULATE_TRACE 0x08
|
||||
#define FM68K_DO_TRACE 0x10
|
||||
|
||||
|
||||
/************************************/
|
||||
/* General library defines */
|
||||
/************************************/
|
||||
|
||||
#ifndef M68K_OK
|
||||
#define M68K_OK 0
|
||||
#endif
|
||||
#ifndef M68K_RUNNING
|
||||
#define M68K_RUNNING 1
|
||||
#endif
|
||||
#ifndef M68K_NO_SUP_ADDR_SPACE
|
||||
#define M68K_NO_SUP_ADDR_SPACE 2
|
||||
#endif
|
||||
#ifndef M68K_DOUBLE_BUS_FAULT
|
||||
#define M68K_DOUBLE_BUS_FAULT -1
|
||||
#endif
|
||||
#ifndef M68K_INV_REG
|
||||
#define M68K_INV_REG -1
|
||||
#endif
|
||||
|
||||
/* Hardware interrupt state */
|
||||
|
||||
#ifndef M68K_IRQ_LEVEL_ERROR
|
||||
#define M68K_IRQ_LEVEL_ERROR -1
|
||||
#endif
|
||||
#ifndef M68K_IRQ_INV_PARAMS
|
||||
#define M68K_IRQ_INV_PARAMS -2
|
||||
#endif
|
||||
|
||||
/* Defines to specify hardware interrupt type */
|
||||
|
||||
#ifndef M68K_AUTOVECTORED_IRQ
|
||||
#define M68K_AUTOVECTORED_IRQ -1
|
||||
#endif
|
||||
#ifndef M68K_SPURIOUS_IRQ
|
||||
#define M68K_SPURIOUS_IRQ -2
|
||||
#endif
|
||||
|
||||
#ifndef M68K_AUTO_LOWER_IRQ
|
||||
#define M68K_AUTO_LOWER_IRQ 1
|
||||
#endif
|
||||
#ifndef M68K_MANUAL_LOWER_IRQ
|
||||
#define M68K_MANUAL_LOWER_IRQ 0
|
||||
#endif
|
||||
|
||||
/* Defines to specify address space */
|
||||
|
||||
#ifndef M68K_SUP_ADDR_SPACE
|
||||
#define M68K_SUP_ADDR_SPACE 0
|
||||
#endif
|
||||
#ifndef M68K_USER_ADDR_SPACE
|
||||
#define M68K_USER_ADDR_SPACE 2
|
||||
#endif
|
||||
#ifndef M68K_PROG_ADDR_SPACE
|
||||
#define M68K_PROG_ADDR_SPACE 0
|
||||
#endif
|
||||
#ifndef M68K_DATA_ADDR_SPACE
|
||||
#define M68K_DATA_ADDR_SPACE 1
|
||||
#endif
|
||||
|
||||
|
||||
/*******************/
|
||||
/* Data definition */
|
||||
/*******************/
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char B;
|
||||
signed char SB;
|
||||
unsigned short W;
|
||||
signed short SW;
|
||||
unsigned int D;
|
||||
signed int SD;
|
||||
} famec_union32;
|
||||
|
||||
/* M68K CPU CONTEXT */
|
||||
typedef struct
|
||||
{
|
||||
unsigned int (*read_byte )(unsigned int a);
|
||||
unsigned int (*read_word )(unsigned int a);
|
||||
unsigned int (*read_long )(unsigned int a);
|
||||
void (*write_byte)(unsigned int a,unsigned char d);
|
||||
void (*write_word)(unsigned int a,unsigned short d);
|
||||
void (*write_long)(unsigned int a,unsigned int d);
|
||||
void (*reset_handler)(void);
|
||||
void (*iack_handler)(unsigned level);
|
||||
famec_union32 dreg[8];
|
||||
famec_union32 areg[8];
|
||||
unsigned asp;
|
||||
unsigned pc;
|
||||
unsigned char interrupts[8];
|
||||
unsigned short sr;
|
||||
unsigned short execinfo;
|
||||
// PD extension
|
||||
int io_cycle_counter; // cycles left
|
||||
|
||||
unsigned int Opcode;
|
||||
signed int cycles_needed;
|
||||
|
||||
unsigned short *PC;
|
||||
unsigned long BasePC;
|
||||
unsigned int flag_C;
|
||||
unsigned int flag_V;
|
||||
unsigned int flag_NotZ;
|
||||
unsigned int flag_N;
|
||||
unsigned int flag_X;
|
||||
unsigned int flag_T;
|
||||
unsigned int flag_S;
|
||||
unsigned int flag_I;
|
||||
|
||||
unsigned char not_polling;
|
||||
unsigned char pad[3];
|
||||
|
||||
unsigned long Fetch[M68K_FETCHBANK1];
|
||||
} M68K_CONTEXT;
|
||||
|
||||
extern M68K_CONTEXT *g_m68kcontext;
|
||||
|
||||
/************************/
|
||||
/* Function definition */
|
||||
/************************/
|
||||
|
||||
/* General purpose functions */
|
||||
void fm68k_init(void);
|
||||
int fm68k_reset(void);
|
||||
int fm68k_emulate(int n, int idle_mode);
|
||||
int fm68k_would_interrupt(void); // to be called from fm68k_emulate()
|
||||
|
||||
unsigned fm68k_get_pc(M68K_CONTEXT *context);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,326 @@
|
|||
#include "../sh2.h"
|
||||
|
||||
#ifdef DRC_CMP
|
||||
#include "../compiler.c"
|
||||
#define BUSY_LOOP_HACKS 0
|
||||
#else
|
||||
#define BUSY_LOOP_HACKS 1
|
||||
#endif
|
||||
|
||||
// MAME types
|
||||
#ifndef INT8
|
||||
typedef signed char INT8;
|
||||
typedef signed short INT16;
|
||||
typedef signed int INT32;
|
||||
typedef unsigned int UINT32;
|
||||
typedef unsigned short UINT16;
|
||||
typedef unsigned char UINT8;
|
||||
#endif
|
||||
|
||||
#ifdef DRC_SH2
|
||||
|
||||
// this nasty conversion is needed for drc-expecting memhandlers
|
||||
#define MAKE_READFUNC(name, cname) \
|
||||
static inline unsigned int name(SH2 *sh2, unsigned int a) \
|
||||
{ \
|
||||
unsigned int ret; \
|
||||
sh2->sr |= sh2->icount << 12; \
|
||||
ret = cname(a, sh2); \
|
||||
sh2->icount = (signed int)sh2->sr >> 12; \
|
||||
sh2->sr &= 0x3f3; \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define MAKE_WRITEFUNC(name, cname) \
|
||||
static inline void name(SH2 *sh2, unsigned int a, unsigned int d) \
|
||||
{ \
|
||||
sh2->sr |= sh2->icount << 12; \
|
||||
cname(a, d, sh2); \
|
||||
sh2->icount = (signed int)sh2->sr >> 12; \
|
||||
sh2->sr &= 0x3f3; \
|
||||
}
|
||||
|
||||
MAKE_READFUNC(RB, p32x_sh2_read8)
|
||||
MAKE_READFUNC(RW, p32x_sh2_read16)
|
||||
MAKE_READFUNC(RL, p32x_sh2_read32)
|
||||
MAKE_WRITEFUNC(WB, p32x_sh2_write8)
|
||||
MAKE_WRITEFUNC(WW, p32x_sh2_write16)
|
||||
MAKE_WRITEFUNC(WL, p32x_sh2_write32)
|
||||
|
||||
#else
|
||||
|
||||
#define RB(sh2, a) p32x_sh2_read8(a, sh2)
|
||||
#define RW(sh2, a) p32x_sh2_read16(a, sh2)
|
||||
#define RL(sh2, a) p32x_sh2_read32(a, sh2)
|
||||
#define WB(sh2, a, d) p32x_sh2_write8(a, d, sh2)
|
||||
#define WW(sh2, a, d) p32x_sh2_write16(a, d, sh2)
|
||||
#define WL(sh2, a, d) p32x_sh2_write32(a, d, sh2)
|
||||
|
||||
#endif
|
||||
|
||||
// some stuff from sh2comn.h
|
||||
#define T 0x00000001
|
||||
#define S 0x00000002
|
||||
#define I 0x000000f0
|
||||
#define Q 0x00000100
|
||||
#define M 0x00000200
|
||||
|
||||
#define AM 0xc7ffffff
|
||||
|
||||
#define FLAGS (M|Q|I|S|T)
|
||||
|
||||
#define Rn ((opcode>>8)&15)
|
||||
#define Rm ((opcode>>4)&15)
|
||||
|
||||
#define sh2_state SH2
|
||||
|
||||
extern void lprintf(const char *fmt, ...);
|
||||
#define logerror lprintf
|
||||
|
||||
#ifdef SH2_STATS
|
||||
static SH2 sh2_stats;
|
||||
static unsigned int op_refs[0x10000];
|
||||
# define LRN 1
|
||||
# define LRM 2
|
||||
# define LRNM (LRN|LRM)
|
||||
# define rlog(rnm) { \
|
||||
int op = opcode; \
|
||||
if ((rnm) & LRN) { \
|
||||
op &= ~0x0f00; \
|
||||
sh2_stats.r[Rn]++; \
|
||||
} \
|
||||
if ((rnm) & LRM) { \
|
||||
op &= ~0x00f0; \
|
||||
sh2_stats.r[Rm]++; \
|
||||
} \
|
||||
op_refs[op]++; \
|
||||
}
|
||||
# define rlog1(x) sh2_stats.r[x]++
|
||||
# define rlog2(x1,x2) sh2_stats.r[x1]++; sh2_stats.r[x2]++
|
||||
#else
|
||||
# define rlog(x)
|
||||
# define rlog1(...)
|
||||
# define rlog2(...)
|
||||
#endif
|
||||
|
||||
#include "sh2.inc"
|
||||
|
||||
#ifndef DRC_CMP
|
||||
|
||||
int sh2_execute_interpreter(SH2 *sh2, int cycles)
|
||||
{
|
||||
UINT32 opcode;
|
||||
|
||||
sh2->icount = cycles;
|
||||
|
||||
if (sh2->icount <= 0)
|
||||
goto out;
|
||||
|
||||
do
|
||||
{
|
||||
if (sh2->delay)
|
||||
{
|
||||
sh2->ppc = sh2->delay;
|
||||
opcode = RW(sh2, sh2->delay);
|
||||
sh2->pc -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh2->ppc = sh2->pc;
|
||||
opcode = RW(sh2, sh2->pc);
|
||||
}
|
||||
|
||||
sh2->delay = 0;
|
||||
sh2->pc += 2;
|
||||
|
||||
switch (opcode & ( 15 << 12))
|
||||
{
|
||||
case 0<<12: op0000(sh2, opcode); break;
|
||||
case 1<<12: op0001(sh2, opcode); break;
|
||||
case 2<<12: op0010(sh2, opcode); break;
|
||||
case 3<<12: op0011(sh2, opcode); break;
|
||||
case 4<<12: op0100(sh2, opcode); break;
|
||||
case 5<<12: op0101(sh2, opcode); break;
|
||||
case 6<<12: op0110(sh2, opcode); break;
|
||||
case 7<<12: op0111(sh2, opcode); break;
|
||||
case 8<<12: op1000(sh2, opcode); break;
|
||||
case 9<<12: op1001(sh2, opcode); break;
|
||||
case 10<<12: op1010(sh2, opcode); break;
|
||||
case 11<<12: op1011(sh2, opcode); break;
|
||||
case 12<<12: op1100(sh2, opcode); break;
|
||||
case 13<<12: op1101(sh2, opcode); break;
|
||||
case 14<<12: op1110(sh2, opcode); break;
|
||||
default: op1111(sh2, opcode); break;
|
||||
}
|
||||
|
||||
sh2->icount--;
|
||||
|
||||
if (sh2->test_irq && !sh2->delay && sh2->pending_level > ((sh2->sr >> 4) & 0x0f))
|
||||
{
|
||||
int level = sh2->pending_level;
|
||||
int vector = sh2->irq_callback(sh2, level);
|
||||
sh2_do_irq(sh2, level, vector);
|
||||
sh2->test_irq = 0;
|
||||
}
|
||||
|
||||
}
|
||||
while (sh2->icount > 0 || sh2->delay); /* can't interrupt before delay */
|
||||
|
||||
out:
|
||||
return sh2->icount;
|
||||
}
|
||||
|
||||
#else // if DRC_CMP
|
||||
|
||||
int sh2_execute_interpreter(SH2 *sh2, int cycles)
|
||||
{
|
||||
static unsigned int base_pc_[2] = { 0, 0 };
|
||||
static unsigned int end_pc_[2] = { 0, 0 };
|
||||
static unsigned char op_flags_[2][BLOCK_INSN_LIMIT];
|
||||
unsigned int *base_pc = &base_pc_[sh2->is_slave];
|
||||
unsigned int *end_pc = &end_pc_[sh2->is_slave];
|
||||
unsigned char *op_flags = op_flags_[sh2->is_slave];
|
||||
unsigned int pc_expect;
|
||||
UINT32 opcode;
|
||||
|
||||
sh2->icount = sh2->cycles_timeslice = cycles;
|
||||
|
||||
if (sh2->pending_level > ((sh2->sr >> 4) & 0x0f))
|
||||
{
|
||||
int level = sh2->pending_level;
|
||||
int vector = sh2->irq_callback(sh2, level);
|
||||
sh2_do_irq(sh2, level, vector);
|
||||
}
|
||||
pc_expect = sh2->pc;
|
||||
|
||||
if (sh2->icount <= 0)
|
||||
goto out;
|
||||
|
||||
do
|
||||
{
|
||||
if (!sh2->delay) {
|
||||
if (sh2->pc < *base_pc || sh2->pc >= *end_pc) {
|
||||
*base_pc = sh2->pc;
|
||||
scan_block(*base_pc, sh2->is_slave,
|
||||
op_flags, end_pc, NULL);
|
||||
}
|
||||
if ((op_flags[(sh2->pc - *base_pc) / 2]
|
||||
& OF_BTARGET) || sh2->pc == *base_pc
|
||||
|| pc_expect != sh2->pc) // branched
|
||||
{
|
||||
pc_expect = sh2->pc;
|
||||
if (sh2->icount < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
do_sh2_trace(sh2, sh2->icount);
|
||||
}
|
||||
pc_expect += 2;
|
||||
|
||||
if (sh2->delay)
|
||||
{
|
||||
sh2->ppc = sh2->delay;
|
||||
opcode = RW(sh2, sh2->delay);
|
||||
sh2->pc -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh2->ppc = sh2->pc;
|
||||
opcode = RW(sh2, sh2->pc);
|
||||
}
|
||||
|
||||
sh2->delay = 0;
|
||||
sh2->pc += 2;
|
||||
|
||||
switch (opcode & ( 15 << 12))
|
||||
{
|
||||
case 0<<12: op0000(sh2, opcode); break;
|
||||
case 1<<12: op0001(sh2, opcode); break;
|
||||
case 2<<12: op0010(sh2, opcode); break;
|
||||
case 3<<12: op0011(sh2, opcode); break;
|
||||
case 4<<12: op0100(sh2, opcode); break;
|
||||
case 5<<12: op0101(sh2, opcode); break;
|
||||
case 6<<12: op0110(sh2, opcode); break;
|
||||
case 7<<12: op0111(sh2, opcode); break;
|
||||
case 8<<12: op1000(sh2, opcode); break;
|
||||
case 9<<12: op1001(sh2, opcode); break;
|
||||
case 10<<12: op1010(sh2, opcode); break;
|
||||
case 11<<12: op1011(sh2, opcode); break;
|
||||
case 12<<12: op1100(sh2, opcode); break;
|
||||
case 13<<12: op1101(sh2, opcode); break;
|
||||
case 14<<12: op1110(sh2, opcode); break;
|
||||
default: op1111(sh2, opcode); break;
|
||||
}
|
||||
|
||||
sh2->icount--;
|
||||
|
||||
if (sh2->test_irq && !sh2->delay && sh2->pending_level > ((sh2->sr >> 4) & 0x0f))
|
||||
{
|
||||
int level = sh2->pending_level;
|
||||
int vector = sh2->irq_callback(sh2, level);
|
||||
sh2_do_irq(sh2, level, vector);
|
||||
sh2->test_irq = 0;
|
||||
}
|
||||
|
||||
}
|
||||
while (1);
|
||||
|
||||
out:
|
||||
return sh2->icount;
|
||||
}
|
||||
|
||||
#endif // DRC_CMP
|
||||
|
||||
#ifdef SH2_STATS
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sh2dasm.h"
|
||||
|
||||
void sh2_dump_stats(void)
|
||||
{
|
||||
static const char *rnames[] = {
|
||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
|
||||
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "SP",
|
||||
"PC", "", "PR", "SR", "GBR", "VBR", "MACH", "MACL"
|
||||
};
|
||||
long long total;
|
||||
char buff[64];
|
||||
int u, i;
|
||||
|
||||
// dump reg usage
|
||||
total = 0;
|
||||
for (i = 0; i < 24; i++)
|
||||
total += sh2_stats.r[i];
|
||||
|
||||
for (i = 0; i < 24; i++) {
|
||||
if (i == 16 || i == 17 || i == 19)
|
||||
continue;
|
||||
printf("r %6.3f%% %-4s %9d\n", (double)sh2_stats.r[i] * 100.0 / total,
|
||||
rnames[i], sh2_stats.r[i]);
|
||||
}
|
||||
|
||||
memset(&sh2_stats, 0, sizeof(sh2_stats));
|
||||
|
||||
// dump ops
|
||||
printf("\n");
|
||||
total = 0;
|
||||
for (i = 0; i < 0x10000; i++)
|
||||
total += op_refs[i];
|
||||
|
||||
for (u = 0; u < 16; u++) {
|
||||
int max = 0, op = 0;
|
||||
for (i = 0; i < 0x10000; i++) {
|
||||
if (op_refs[i] > max) {
|
||||
max = op_refs[i];
|
||||
op = i;
|
||||
}
|
||||
}
|
||||
DasmSH2(buff, 0, op);
|
||||
printf("i %6.3f%% %9d %s\n", (double)op_refs[op] * 100.0 / total,
|
||||
op_refs[op], buff);
|
||||
op_refs[op] = 0;
|
||||
}
|
||||
memset(op_refs, 0, sizeof(op_refs));
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2009,2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "sh2.h"
|
||||
|
||||
#define I 0xf0
|
||||
|
||||
int sh2_init(SH2 *sh2, int is_slave, SH2 *other_sh2)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int mult_m68k_to_sh2 = sh2->mult_m68k_to_sh2;
|
||||
unsigned int mult_sh2_to_m68k = sh2->mult_sh2_to_m68k;
|
||||
|
||||
memset(sh2, 0, sizeof(*sh2));
|
||||
sh2->is_slave = is_slave;
|
||||
sh2->other_sh2 = other_sh2;
|
||||
sh2->mult_m68k_to_sh2 = mult_m68k_to_sh2;
|
||||
sh2->mult_sh2_to_m68k = mult_sh2_to_m68k;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sh2_finish(SH2 *sh2)
|
||||
{
|
||||
#ifdef DRC_SH2
|
||||
sh2_drc_finish(sh2);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sh2_reset(SH2 *sh2)
|
||||
{
|
||||
sh2->pc = p32x_sh2_read32(0, sh2);
|
||||
sh2->r[15] = p32x_sh2_read32(4, sh2);
|
||||
sh2->sr = I;
|
||||
sh2->vbr = 0;
|
||||
sh2->pending_int_irq = 0;
|
||||
}
|
||||
|
||||
void sh2_do_irq(SH2 *sh2, int level, int vector)
|
||||
{
|
||||
sh2->sr &= 0x3f3;
|
||||
|
||||
sh2->r[15] -= 4;
|
||||
p32x_sh2_write32(sh2->r[15], sh2->sr, sh2); /* push SR onto stack */
|
||||
sh2->r[15] -= 4;
|
||||
p32x_sh2_write32(sh2->r[15], sh2->pc, sh2); /* push PC onto stack */
|
||||
|
||||
/* set I flags in SR */
|
||||
sh2->sr = (sh2->sr & ~I) | (level << 4);
|
||||
|
||||
/* fetch PC */
|
||||
sh2->pc = p32x_sh2_read32(sh2->vbr + vector * 4, sh2);
|
||||
|
||||
/* 13 cycles at best */
|
||||
sh2->icount -= 13;
|
||||
}
|
||||
|
||||
int sh2_irl_irq(SH2 *sh2, int level, int nested_call)
|
||||
{
|
||||
int taken;
|
||||
|
||||
sh2->pending_irl = level;
|
||||
if (level < sh2->pending_int_irq)
|
||||
level = sh2->pending_int_irq;
|
||||
sh2->pending_level = level;
|
||||
|
||||
taken = (level > ((sh2->sr >> 4) & 0x0f));
|
||||
if (taken) {
|
||||
if (!nested_call) {
|
||||
// not in memhandler, so handle this now (recompiler friendly)
|
||||
// do this to avoid missing irqs that other SH2 might clear
|
||||
int vector = sh2->irq_callback(sh2, level);
|
||||
sh2_do_irq(sh2, level, vector);
|
||||
sh2->m68krcycles_done += C_SH2_TO_M68K(*sh2, 13);
|
||||
}
|
||||
else
|
||||
sh2->test_irq = 1;
|
||||
}
|
||||
return taken;
|
||||
}
|
||||
|
||||
void sh2_internal_irq(SH2 *sh2, int level, int vector)
|
||||
{
|
||||
// FIXME: multiple internal irqs not handled..
|
||||
// assuming internal irqs never clear until accepted
|
||||
sh2->pending_int_irq = level;
|
||||
sh2->pending_int_vector = vector;
|
||||
if (level > sh2->pending_level)
|
||||
sh2->pending_level = level;
|
||||
|
||||
sh2->test_irq = 1;
|
||||
}
|
||||
|
||||
#define SH2_REG_SIZE (offsetof(SH2, macl) + sizeof(sh2->macl))
|
||||
|
||||
void sh2_pack(const SH2 *sh2, unsigned char *buff)
|
||||
{
|
||||
unsigned int *p;
|
||||
|
||||
memcpy(buff, sh2, SH2_REG_SIZE);
|
||||
p = (void *)(buff + SH2_REG_SIZE);
|
||||
|
||||
p[0] = sh2->pending_int_irq;
|
||||
p[1] = sh2->pending_int_vector;
|
||||
}
|
||||
|
||||
void sh2_unpack(SH2 *sh2, const unsigned char *buff)
|
||||
{
|
||||
unsigned int *p;
|
||||
|
||||
memcpy(sh2, buff, SH2_REG_SIZE);
|
||||
p = (void *)(buff + SH2_REG_SIZE);
|
||||
|
||||
sh2->pending_int_irq = p[0];
|
||||
sh2->pending_int_vector = p[1];
|
||||
sh2->test_irq = 1;
|
||||
}
|
||||
|
||||
#ifdef DRC_CMP
|
||||
|
||||
/* trace/compare */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pico/memory.h>
|
||||
#undef _USE_CZ80 // HACK
|
||||
#include <pico/pico_int.h>
|
||||
#include <pico/debug.h>
|
||||
|
||||
static SH2 sh2ref[2];
|
||||
static unsigned int mem_val;
|
||||
|
||||
static unsigned int local_read32(SH2 *sh2, u32 a)
|
||||
{
|
||||
const sh2_memmap *sh2_map = sh2->read16_map;
|
||||
u16 *pd;
|
||||
uptr p;
|
||||
|
||||
sh2_map += (a >> 25);
|
||||
p = sh2_map->addr;
|
||||
if (!map_flag_set(p)) {
|
||||
pd = (u16 *)((p << 1) + ((a & sh2_map->mask) & ~1));
|
||||
return (pd[0] << 16) | pd[1];
|
||||
}
|
||||
|
||||
if ((a & 0xfffff000) == 0xc0000000) {
|
||||
// data array
|
||||
pd = (u16 *)sh2->data_array + (a & 0xfff) / 2;
|
||||
return (pd[0] << 16) | pd[1];
|
||||
}
|
||||
if ((a & 0xdfffffc0) == 0x4000) {
|
||||
pd = &Pico32x.regs[(a & 0x3f) / 2];
|
||||
return (pd[0] << 16) | pd[1];
|
||||
}
|
||||
if ((a & 0xdffffe00) == 0x4200) {
|
||||
pd = &Pico32xMem->pal[(a & 0x1ff) / 2];
|
||||
return (pd[0] << 16) | pd[1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void do_sh2_trace(SH2 *current, int cycles)
|
||||
{
|
||||
static int current_slave = -1;
|
||||
static u32 current_m68k_pc;
|
||||
SH2 *sh2o = &sh2ref[current->is_slave];
|
||||
u32 *regs_a = (void *)current;
|
||||
u32 *regs_o = (void *)sh2o;
|
||||
unsigned char v;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
if (SekPc != current_m68k_pc) {
|
||||
current_m68k_pc = SekPc;
|
||||
tl_write_uint(CTL_M68KPC, current_m68k_pc);
|
||||
}
|
||||
|
||||
if (current->is_slave != current_slave) {
|
||||
current_slave = current->is_slave;
|
||||
v = CTL_MASTERSLAVE | current->is_slave;
|
||||
tl_write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
for (i = 0; i < offsetof(SH2, read8_map) / 4; i++) {
|
||||
if (i == 17) // ppc
|
||||
continue;
|
||||
if (regs_a[i] != regs_o[i]) {
|
||||
tl_write_uint(CTL_SH2_R + i, regs_a[i]);
|
||||
regs_o[i] = regs_a[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (current->ea != sh2o->ea) {
|
||||
tl_write_uint(CTL_EA, current->ea);
|
||||
sh2o->ea = current->ea;
|
||||
}
|
||||
val = local_read32(current, current->ea);
|
||||
if (mem_val != val) {
|
||||
tl_write_uint(CTL_EAVAL, val);
|
||||
mem_val = val;
|
||||
}
|
||||
tl_write_uint(CTL_CYCLES, cycles);
|
||||
}
|
||||
|
||||
static const char *regnames[] = {
|
||||
"r0", "r1", "r2", "r3",
|
||||
"r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11",
|
||||
"r12", "r13", "r14", "r15",
|
||||
"pc", "ppc", "pr", "sr",
|
||||
"gbr", "vbr", "mach","macl",
|
||||
};
|
||||
|
||||
static void dump_regs(SH2 *sh2)
|
||||
{
|
||||
char csh2;
|
||||
int i;
|
||||
|
||||
csh2 = sh2->is_slave ? 's' : 'm';
|
||||
for (i = 0; i < 16/2; i++)
|
||||
printf("%csh2 r%d: %08x r%02d: %08x\n", csh2,
|
||||
i, sh2->r[i], i+8, sh2->r[i+8]);
|
||||
printf("%csh2 PC: %08x , %08x\n", csh2, sh2->pc, sh2->ppc);
|
||||
printf("%csh2 SR: %03x PR: %08x\n", csh2, sh2->sr, sh2->pr);
|
||||
}
|
||||
|
||||
void do_sh2_cmp(SH2 *current)
|
||||
{
|
||||
static int current_slave;
|
||||
static u32 current_val;
|
||||
SH2 *sh2o = &sh2ref[current->is_slave];
|
||||
u32 *regs_a = (void *)current;
|
||||
u32 *regs_o = (void *)sh2o;
|
||||
unsigned char code;
|
||||
int cycles_o = 666;
|
||||
u32 sr, val;
|
||||
int bad = 0;
|
||||
int cycles;
|
||||
int i, ret;
|
||||
|
||||
sh2ref[1].is_slave = 1;
|
||||
|
||||
while (1) {
|
||||
ret = tl_read(&code, 1);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (code == CTL_CYCLES) {
|
||||
tl_read(&cycles_o, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case CTL_MASTERSLAVE:
|
||||
case CTL_MASTERSLAVE + 1:
|
||||
current_slave = code & 1;
|
||||
break;
|
||||
case CTL_EA:
|
||||
tl_read_uint(&sh2o->ea);
|
||||
break;
|
||||
case CTL_EAVAL:
|
||||
tl_read_uint(¤t_val);
|
||||
break;
|
||||
case CTL_M68KPC:
|
||||
tl_read_uint(&val);
|
||||
if (SekPc != val) {
|
||||
printf("m68k: %08x %08x\n", SekPc, val);
|
||||
bad = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (CTL_SH2_R <= code && code < CTL_SH2_R +
|
||||
offsetof(SH2, read8_map) / 4)
|
||||
{
|
||||
tl_read_uint(regs_o + code - CTL_SH2_R);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("wrong code: %02x\n", code);
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
printf("EOF?\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (current->is_slave != current_slave) {
|
||||
printf("bad slave: %d %d\n", current->is_slave,
|
||||
current_slave);
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < offsetof(SH2, read8_map) / 4; i++) {
|
||||
if (i == 17 || i == 19) // ppc, sr
|
||||
continue;
|
||||
if (regs_a[i] != regs_o[i]) {
|
||||
printf("bad %4s: %08x %08x\n",
|
||||
regnames[i], regs_a[i], regs_o[i]);
|
||||
bad = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sr = current->sr & 0x3f3;
|
||||
cycles = (signed int)current->sr >> 12;
|
||||
|
||||
if (sr != sh2o->sr) {
|
||||
printf("bad SR: %03x %03x\n", sr, sh2o->sr);
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
if (cycles != cycles_o) {
|
||||
printf("bad cycles: %d %d\n", cycles, cycles_o);
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
val = local_read32(current, sh2o->ea);
|
||||
if (val != current_val) {
|
||||
printf("bad val @%08x: %08x %08x\n", sh2o->ea, val, current_val);
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
if (!bad) {
|
||||
sh2o->ppc = current->pc;
|
||||
return;
|
||||
}
|
||||
|
||||
end:
|
||||
printf("--\n");
|
||||
dump_regs(sh2o);
|
||||
if (current->is_slave != current_slave)
|
||||
dump_regs(&sh2ref[current->is_slave ^ 1]);
|
||||
PDebugDumpMem();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif // DRC_CMP
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef __SH2_H__
|
||||
#define __SH2_H__
|
||||
|
||||
#if !defined(REGPARM) && defined(__i386__)
|
||||
#define REGPARM(x) __attribute__((regparm(x)))
|
||||
#else
|
||||
#define REGPARM(x)
|
||||
#endif
|
||||
|
||||
// registers - matches structure order
|
||||
typedef enum {
|
||||
SHR_R0 = 0, SHR_SP = 15,
|
||||
SHR_PC, SHR_PPC, SHR_PR, SHR_SR,
|
||||
SHR_GBR, SHR_VBR, SHR_MACH, SHR_MACL,
|
||||
} sh2_reg_e;
|
||||
|
||||
typedef struct SH2_
|
||||
{
|
||||
unsigned int r[16]; // 00
|
||||
unsigned int pc; // 40
|
||||
unsigned int ppc;
|
||||
unsigned int pr;
|
||||
unsigned int sr;
|
||||
unsigned int gbr, vbr; // 50
|
||||
unsigned int mach, macl; // 58
|
||||
|
||||
// common
|
||||
const void *read8_map; // 60
|
||||
const void *read16_map;
|
||||
const void **write8_tab;
|
||||
const void **write16_tab;
|
||||
|
||||
#define SH2_STATE_RUN (1 << 0) // to prevent recursion
|
||||
#define SH2_STATE_SLEEP (1 << 1)
|
||||
#define SH2_STATE_CPOLL (1 << 2) // polling comm regs
|
||||
#define SH2_STATE_VPOLL (1 << 3) // polling VDP
|
||||
unsigned int state;
|
||||
unsigned int poll_addr;
|
||||
int poll_cycles;
|
||||
int poll_cnt;
|
||||
|
||||
// interpreter stuff
|
||||
int icount; // cycles left in current timeslice
|
||||
unsigned int ea;
|
||||
unsigned int delay;
|
||||
unsigned int test_irq;
|
||||
|
||||
int pending_level; // MAX(pending_irl, pending_int_irq)
|
||||
int pending_irl;
|
||||
int pending_int_irq; // internal irq
|
||||
int pending_int_vector;
|
||||
int REGPARM(2) (*irq_callback)(struct SH2_ *sh2, int level);
|
||||
int is_slave;
|
||||
|
||||
unsigned int cycles_timeslice;
|
||||
|
||||
struct SH2_ *other_sh2;
|
||||
|
||||
// we use 68k reference cycles for easier sync
|
||||
unsigned int m68krcycles_done;
|
||||
unsigned int mult_m68k_to_sh2;
|
||||
unsigned int mult_sh2_to_m68k;
|
||||
|
||||
unsigned char data_array[0x1000]; // cache (can be used as RAM)
|
||||
unsigned int peri_regs[0x200/4]; // periphereal regs
|
||||
} SH2;
|
||||
|
||||
#define CYCLE_MULT_SHIFT 10
|
||||
#define C_M68K_TO_SH2(xsh2, c) \
|
||||
((int)((c) * (xsh2).mult_m68k_to_sh2) >> CYCLE_MULT_SHIFT)
|
||||
#define C_SH2_TO_M68K(xsh2, c) \
|
||||
((int)((c + 3) * (xsh2).mult_sh2_to_m68k) >> CYCLE_MULT_SHIFT)
|
||||
|
||||
int sh2_init(SH2 *sh2, int is_slave, SH2 *other_sh2);
|
||||
void sh2_finish(SH2 *sh2);
|
||||
void sh2_reset(SH2 *sh2);
|
||||
int sh2_irl_irq(SH2 *sh2, int level, int nested_call);
|
||||
void sh2_internal_irq(SH2 *sh2, int level, int vector);
|
||||
void sh2_do_irq(SH2 *sh2, int level, int vector);
|
||||
void sh2_pack(const SH2 *sh2, unsigned char *buff);
|
||||
void sh2_unpack(SH2 *sh2, const unsigned char *buff);
|
||||
|
||||
int sh2_execute_drc(SH2 *sh2c, int cycles);
|
||||
int sh2_execute_interpreter(SH2 *sh2c, int cycles);
|
||||
|
||||
static inline int sh2_execute(SH2 *sh2, int cycles, int use_drc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sh2->cycles_timeslice = cycles;
|
||||
#ifdef DRC_SH2
|
||||
if (use_drc)
|
||||
ret = sh2_execute_drc(sh2, cycles);
|
||||
else
|
||||
#endif
|
||||
ret = sh2_execute_interpreter(sh2, cycles);
|
||||
|
||||
return sh2->cycles_timeslice - ret;
|
||||
}
|
||||
|
||||
// regs, pending_int*, cycles, reserved
|
||||
#define SH2_STATE_SIZE ((24 + 2 + 2 + 12) * 4)
|
||||
|
||||
// pico memhandlers
|
||||
// XXX: move somewhere else
|
||||
unsigned int REGPARM(2) p32x_sh2_read8(unsigned int a, SH2 *sh2);
|
||||
unsigned int REGPARM(2) p32x_sh2_read16(unsigned int a, SH2 *sh2);
|
||||
unsigned int REGPARM(2) p32x_sh2_read32(unsigned int a, SH2 *sh2);
|
||||
void REGPARM(3) p32x_sh2_write8 (unsigned int a, unsigned int d, SH2 *sh2);
|
||||
void REGPARM(3) p32x_sh2_write16(unsigned int a, unsigned int d, SH2 *sh2);
|
||||
void REGPARM(3) p32x_sh2_write32(unsigned int a, unsigned int d, SH2 *sh2);
|
||||
|
||||
// debug
|
||||
#ifdef DRC_CMP
|
||||
void do_sh2_trace(SH2 *current, int cycles);
|
||||
void do_sh2_cmp(SH2 *current);
|
||||
#endif
|
||||
|
||||
#endif /* __SH2_H__ */
|
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2009,2010,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "../pico_int.h"
|
||||
#include "../sound/ym2612.h"
|
||||
|
||||
struct Pico32x Pico32x;
|
||||
SH2 sh2s[2];
|
||||
|
||||
#define SH2_IDLE_STATES (SH2_STATE_CPOLL|SH2_STATE_VPOLL|SH2_STATE_SLEEP)
|
||||
|
||||
static int REGPARM(2) sh2_irq_cb(SH2 *sh2, int level)
|
||||
{
|
||||
if (sh2->pending_irl > sh2->pending_int_irq) {
|
||||
elprintf_sh2(sh2, EL_32X, "ack/irl %d @ %08x",
|
||||
level, sh2_pc(sh2));
|
||||
return 64 + sh2->pending_irl / 2;
|
||||
} else {
|
||||
elprintf_sh2(sh2, EL_32X, "ack/int %d/%d @ %08x",
|
||||
level, sh2->pending_int_vector, sh2_pc(sh2));
|
||||
sh2->pending_int_irq = 0; // auto-clear
|
||||
sh2->pending_level = sh2->pending_irl;
|
||||
return sh2->pending_int_vector;
|
||||
}
|
||||
}
|
||||
|
||||
// MUST specify active_sh2 when called from sh2 memhandlers
|
||||
void p32x_update_irls(SH2 *active_sh2, int m68k_cycles)
|
||||
{
|
||||
int irqs, mlvl = 0, slvl = 0;
|
||||
int mrun, srun;
|
||||
|
||||
if (active_sh2 != NULL)
|
||||
m68k_cycles = sh2_cycles_done_m68k(active_sh2);
|
||||
|
||||
// msh2
|
||||
irqs = Pico32x.sh2irqs | Pico32x.sh2irqi[0];
|
||||
while ((irqs >>= 1))
|
||||
mlvl++;
|
||||
mlvl *= 2;
|
||||
|
||||
// ssh2
|
||||
irqs = Pico32x.sh2irqs | Pico32x.sh2irqi[1];
|
||||
while ((irqs >>= 1))
|
||||
slvl++;
|
||||
slvl *= 2;
|
||||
|
||||
mrun = sh2_irl_irq(&msh2, mlvl, active_sh2 == &msh2);
|
||||
if (mrun) {
|
||||
p32x_sh2_poll_event(&msh2, SH2_IDLE_STATES, m68k_cycles);
|
||||
if (active_sh2 == &msh2)
|
||||
sh2_end_run(active_sh2, 1);
|
||||
}
|
||||
|
||||
srun = sh2_irl_irq(&ssh2, slvl, active_sh2 == &ssh2);
|
||||
if (srun) {
|
||||
p32x_sh2_poll_event(&ssh2, SH2_IDLE_STATES, m68k_cycles);
|
||||
if (active_sh2 == &ssh2)
|
||||
sh2_end_run(active_sh2, 1);
|
||||
}
|
||||
|
||||
elprintf(EL_32X, "update_irls: m %d/%d, s %d/%d", mlvl, mrun, slvl, srun);
|
||||
}
|
||||
|
||||
// the mask register is inconsistent, CMD is supposed to be a mask,
|
||||
// while others are actually irq trigger enables?
|
||||
// TODO: test on hw..
|
||||
void p32x_trigger_irq(SH2 *sh2, int m68k_cycles, unsigned int mask)
|
||||
{
|
||||
Pico32x.sh2irqs |= mask & P32XI_VRES;
|
||||
Pico32x.sh2irqi[0] |= mask & (Pico32x.sh2irq_mask[0] << 3);
|
||||
Pico32x.sh2irqi[1] |= mask & (Pico32x.sh2irq_mask[1] << 3);
|
||||
|
||||
p32x_update_irls(sh2, m68k_cycles);
|
||||
}
|
||||
|
||||
void p32x_update_cmd_irq(SH2 *sh2, int m68k_cycles)
|
||||
{
|
||||
if ((Pico32x.sh2irq_mask[0] & 2) && (Pico32x.regs[2 / 2] & 1))
|
||||
Pico32x.sh2irqi[0] |= P32XI_CMD;
|
||||
else
|
||||
Pico32x.sh2irqi[0] &= ~P32XI_CMD;
|
||||
|
||||
if ((Pico32x.sh2irq_mask[1] & 2) && (Pico32x.regs[2 / 2] & 2))
|
||||
Pico32x.sh2irqi[1] |= P32XI_CMD;
|
||||
else
|
||||
Pico32x.sh2irqi[1] &= ~P32XI_CMD;
|
||||
|
||||
p32x_update_irls(sh2, m68k_cycles);
|
||||
}
|
||||
|
||||
void Pico32xStartup(void)
|
||||
{
|
||||
elprintf(EL_STATUS|EL_32X, "32X startup");
|
||||
|
||||
// TODO: OOM handling
|
||||
PicoAHW |= PAHW_32X;
|
||||
sh2_init(&msh2, 0, &ssh2);
|
||||
msh2.irq_callback = sh2_irq_cb;
|
||||
sh2_init(&ssh2, 1, &msh2);
|
||||
ssh2.irq_callback = sh2_irq_cb;
|
||||
|
||||
PicoMemSetup32x();
|
||||
p32x_pwm_ctl_changed();
|
||||
p32x_timers_recalc();
|
||||
|
||||
Pico32x.sh2_regs[0] = P32XS2_ADEN;
|
||||
if (Pico.m.ncart_in)
|
||||
Pico32x.sh2_regs[0] |= P32XS_nCART;
|
||||
|
||||
if (!Pico.m.pal)
|
||||
Pico32x.vdp_regs[0] |= P32XV_nPAL;
|
||||
|
||||
rendstatus_old = -1;
|
||||
|
||||
emu_32x_startup();
|
||||
}
|
||||
|
||||
#define HWSWAP(x) (((x) << 16) | ((x) >> 16))
|
||||
void p32x_reset_sh2s(void)
|
||||
{
|
||||
elprintf(EL_32X, "sh2 reset");
|
||||
|
||||
sh2_reset(&msh2);
|
||||
sh2_reset(&ssh2);
|
||||
sh2_peripheral_reset(&msh2);
|
||||
sh2_peripheral_reset(&ssh2);
|
||||
|
||||
// if we don't have BIOS set, perform it's work here.
|
||||
// MSH2
|
||||
if (p32x_bios_m == NULL) {
|
||||
unsigned int idl_src, idl_dst, idl_size; // initial data load
|
||||
unsigned int vbr;
|
||||
|
||||
// initial data
|
||||
idl_src = HWSWAP(*(unsigned int *)(Pico.rom + 0x3d4)) & ~0xf0000000;
|
||||
idl_dst = HWSWAP(*(unsigned int *)(Pico.rom + 0x3d8)) & ~0xf0000000;
|
||||
idl_size= HWSWAP(*(unsigned int *)(Pico.rom + 0x3dc));
|
||||
if (idl_size > Pico.romsize || idl_src + idl_size > Pico.romsize ||
|
||||
idl_size > 0x40000 || idl_dst + idl_size > 0x40000 || (idl_src & 3) || (idl_dst & 3)) {
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "32x: invalid initial data ptrs: %06x -> %06x, %06x",
|
||||
idl_src, idl_dst, idl_size);
|
||||
}
|
||||
else
|
||||
memcpy(Pico32xMem->sdram + idl_dst, Pico.rom + idl_src, idl_size);
|
||||
|
||||
// GBR/VBR
|
||||
vbr = HWSWAP(*(unsigned int *)(Pico.rom + 0x3e8));
|
||||
sh2_set_gbr(0, 0x20004000);
|
||||
sh2_set_vbr(0, vbr);
|
||||
|
||||
// checksum and M_OK
|
||||
Pico32x.regs[0x28 / 2] = *(unsigned short *)(Pico.rom + 0x18e);
|
||||
// program will set M_OK
|
||||
}
|
||||
|
||||
// SSH2
|
||||
if (p32x_bios_s == NULL) {
|
||||
unsigned int vbr;
|
||||
|
||||
// GBR/VBR
|
||||
vbr = HWSWAP(*(unsigned int *)(Pico.rom + 0x3ec));
|
||||
sh2_set_gbr(1, 0x20004000);
|
||||
sh2_set_vbr(1, vbr);
|
||||
// program will set S_OK
|
||||
}
|
||||
|
||||
msh2.m68krcycles_done = ssh2.m68krcycles_done = SekCyclesDone();
|
||||
}
|
||||
|
||||
void Pico32xInit(void)
|
||||
{
|
||||
if (msh2.mult_m68k_to_sh2 == 0 || msh2.mult_sh2_to_m68k == 0)
|
||||
Pico32xSetClocks(PICO_MSH2_HZ, 0);
|
||||
if (ssh2.mult_m68k_to_sh2 == 0 || ssh2.mult_sh2_to_m68k == 0)
|
||||
Pico32xSetClocks(0, PICO_MSH2_HZ);
|
||||
}
|
||||
|
||||
void PicoPower32x(void)
|
||||
{
|
||||
memset(&Pico32x, 0, sizeof(Pico32x));
|
||||
|
||||
Pico32x.regs[0] = P32XS_REN|P32XS_nRES; // verified
|
||||
Pico32x.vdp_regs[0x0a/2] = P32XV_VBLK|P32XV_PEN;
|
||||
}
|
||||
|
||||
void PicoReset32x(void)
|
||||
{
|
||||
if (PicoAHW & PAHW_32X) {
|
||||
p32x_trigger_irq(NULL, SekCyclesDone(), P32XI_VRES);
|
||||
p32x_sh2_poll_event(&msh2, SH2_IDLE_STATES, 0);
|
||||
p32x_sh2_poll_event(&ssh2, SH2_IDLE_STATES, 0);
|
||||
p32x_pwm_ctl_changed();
|
||||
p32x_timers_recalc();
|
||||
}
|
||||
}
|
||||
|
||||
static void p32x_start_blank(void)
|
||||
{
|
||||
if (Pico32xDrawMode != PDM32X_OFF && !PicoSkipFrame) {
|
||||
int offs, lines;
|
||||
|
||||
pprof_start(draw);
|
||||
|
||||
offs = 8; lines = 224;
|
||||
if ((Pico.video.reg[1] & 8) && !(PicoOpt & POPT_ALT_RENDERER)) {
|
||||
offs = 0;
|
||||
lines = 240;
|
||||
}
|
||||
|
||||
// XXX: no proper handling of 32col mode..
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) != 0 && // 32x not blanking
|
||||
(Pico.video.reg[12] & 1) && // 40col mode
|
||||
(PicoDrawMask & PDRAW_32X_ON))
|
||||
{
|
||||
int md_bg = Pico.video.reg[7] & 0x3f;
|
||||
|
||||
// we draw full layer (not line-by-line)
|
||||
PicoDraw32xLayer(offs, lines, md_bg);
|
||||
}
|
||||
else if (Pico32xDrawMode != PDM32X_32X_ONLY)
|
||||
PicoDraw32xLayerMdOnly(offs, lines);
|
||||
|
||||
pprof_end(draw);
|
||||
}
|
||||
|
||||
// enter vblank
|
||||
Pico32x.vdp_regs[0x0a/2] |= P32XV_VBLK|P32XV_PEN;
|
||||
|
||||
// FB swap waits until vblank
|
||||
if ((Pico32x.vdp_regs[0x0a/2] ^ Pico32x.pending_fb) & P32XV_FS) {
|
||||
Pico32x.vdp_regs[0x0a/2] &= ~P32XV_FS;
|
||||
Pico32x.vdp_regs[0x0a/2] |= Pico32x.pending_fb;
|
||||
Pico32xSwapDRAM(Pico32x.pending_fb ^ 1);
|
||||
}
|
||||
|
||||
p32x_trigger_irq(NULL, SekCyclesDone(), P32XI_VINT);
|
||||
p32x_sh2_poll_event(&msh2, SH2_STATE_VPOLL, 0);
|
||||
p32x_sh2_poll_event(&ssh2, SH2_STATE_VPOLL, 0);
|
||||
}
|
||||
|
||||
void p32x_schedule_hint(SH2 *sh2, int m68k_cycles)
|
||||
{
|
||||
// rather rough, 32x hint is useless in practice
|
||||
int after;
|
||||
|
||||
if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 4))
|
||||
return; // nobody cares
|
||||
// note: when Pico.m.scanline is 224, SH2s might
|
||||
// still be at scanline 93 (or so)
|
||||
if (!(Pico32x.sh2_regs[0] & 0x80) && Pico.m.scanline > 224)
|
||||
return;
|
||||
|
||||
after = (Pico32x.sh2_regs[4 / 2] + 1) * 488;
|
||||
if (sh2 != NULL)
|
||||
p32x_event_schedule_sh2(sh2, P32X_EVENT_HINT, after);
|
||||
else
|
||||
p32x_event_schedule(m68k_cycles, P32X_EVENT_HINT, after);
|
||||
}
|
||||
|
||||
/* events */
|
||||
static void fillend_event(unsigned int now)
|
||||
{
|
||||
Pico32x.vdp_regs[0x0a/2] &= ~P32XV_nFEN;
|
||||
p32x_sh2_poll_event(&msh2, SH2_STATE_VPOLL, now);
|
||||
p32x_sh2_poll_event(&ssh2, SH2_STATE_VPOLL, now);
|
||||
}
|
||||
|
||||
static void hint_event(unsigned int now)
|
||||
{
|
||||
p32x_trigger_irq(NULL, now, P32XI_HINT);
|
||||
p32x_schedule_hint(NULL, now);
|
||||
}
|
||||
|
||||
typedef void (event_cb)(unsigned int now);
|
||||
|
||||
/* times are in m68k (7.6MHz) cycles */
|
||||
unsigned int p32x_event_times[P32X_EVENT_COUNT];
|
||||
static unsigned int event_time_next;
|
||||
static event_cb *p32x_event_cbs[P32X_EVENT_COUNT] = {
|
||||
[P32X_EVENT_PWM] = p32x_pwm_irq_event,
|
||||
[P32X_EVENT_FILLEND] = fillend_event,
|
||||
[P32X_EVENT_HINT] = hint_event,
|
||||
};
|
||||
|
||||
// schedule event at some time 'after', in m68k clocks
|
||||
void p32x_event_schedule(unsigned int now, enum p32x_event event, int after)
|
||||
{
|
||||
unsigned int when;
|
||||
|
||||
when = (now + after) | 1;
|
||||
|
||||
elprintf(EL_32X, "32x: new event #%u %u->%u", event, now, when);
|
||||
p32x_event_times[event] = when;
|
||||
|
||||
if (event_time_next == 0 || CYCLES_GT(event_time_next, when))
|
||||
event_time_next = when;
|
||||
}
|
||||
|
||||
void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after)
|
||||
{
|
||||
unsigned int now = sh2_cycles_done_m68k(sh2);
|
||||
int left_to_next;
|
||||
|
||||
p32x_event_schedule(now, event, after);
|
||||
|
||||
left_to_next = (event_time_next - now) * 3;
|
||||
sh2_end_run(sh2, left_to_next);
|
||||
}
|
||||
|
||||
static void p32x_run_events(unsigned int until)
|
||||
{
|
||||
int oldest, oldest_diff, time;
|
||||
int i, diff;
|
||||
|
||||
while (1) {
|
||||
oldest = -1, oldest_diff = 0x7fffffff;
|
||||
|
||||
for (i = 0; i < P32X_EVENT_COUNT; i++) {
|
||||
if (p32x_event_times[i]) {
|
||||
diff = p32x_event_times[i] - until;
|
||||
if (diff < oldest_diff) {
|
||||
oldest_diff = diff;
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldest_diff <= 0) {
|
||||
time = p32x_event_times[oldest];
|
||||
p32x_event_times[oldest] = 0;
|
||||
elprintf(EL_32X, "32x: run event #%d %u", oldest, time);
|
||||
p32x_event_cbs[oldest](time);
|
||||
}
|
||||
else if (oldest_diff < 0x7fffffff) {
|
||||
event_time_next = p32x_event_times[oldest];
|
||||
break;
|
||||
}
|
||||
else {
|
||||
event_time_next = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldest != -1)
|
||||
elprintf(EL_32X, "32x: next event #%d at %u",
|
||||
oldest, event_time_next);
|
||||
}
|
||||
|
||||
static inline void run_sh2(SH2 *sh2, int m68k_cycles)
|
||||
{
|
||||
int cycles, done;
|
||||
|
||||
pevt_log_sh2_o(sh2, EVT_RUN_START);
|
||||
sh2->state |= SH2_STATE_RUN;
|
||||
cycles = C_M68K_TO_SH2(*sh2, m68k_cycles);
|
||||
elprintf_sh2(sh2, EL_32X, "+run %u %d @%08x",
|
||||
sh2->m68krcycles_done, cycles, sh2->pc);
|
||||
|
||||
done = sh2_execute(sh2, cycles, PicoOpt & POPT_EN_DRC);
|
||||
|
||||
sh2->m68krcycles_done += C_SH2_TO_M68K(*sh2, done);
|
||||
sh2->state &= ~SH2_STATE_RUN;
|
||||
pevt_log_sh2_o(sh2, EVT_RUN_END);
|
||||
elprintf_sh2(sh2, EL_32X, "-run %u %d",
|
||||
sh2->m68krcycles_done, done);
|
||||
}
|
||||
|
||||
// sync other sh2 to this one
|
||||
// note: recursive call
|
||||
void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target)
|
||||
{
|
||||
SH2 *osh2 = sh2->other_sh2;
|
||||
int left_to_event;
|
||||
int m68k_cycles;
|
||||
|
||||
if (osh2->state & SH2_STATE_RUN)
|
||||
return;
|
||||
|
||||
m68k_cycles = m68k_target - osh2->m68krcycles_done;
|
||||
if (m68k_cycles < 200)
|
||||
return;
|
||||
|
||||
if (osh2->state & SH2_IDLE_STATES) {
|
||||
osh2->m68krcycles_done = m68k_target;
|
||||
return;
|
||||
}
|
||||
|
||||
elprintf_sh2(osh2, EL_32X, "sync to %u %d",
|
||||
m68k_target, m68k_cycles);
|
||||
|
||||
run_sh2(osh2, m68k_cycles);
|
||||
|
||||
// there might be new event to schedule current sh2 to
|
||||
if (event_time_next) {
|
||||
left_to_event = event_time_next - m68k_target;
|
||||
left_to_event *= 3;
|
||||
if (sh2_cycles_left(sh2) > left_to_event) {
|
||||
if (left_to_event < 1)
|
||||
left_to_event = 1;
|
||||
sh2_end_run(sh2, left_to_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define sync_sh2s_normal p32x_sync_sh2s
|
||||
//#define sync_sh2s_lockstep p32x_sync_sh2s
|
||||
|
||||
/* most timing is in 68k clock */
|
||||
void sync_sh2s_normal(unsigned int m68k_target)
|
||||
{
|
||||
unsigned int now, target, timer_cycles;
|
||||
int cycles;
|
||||
|
||||
elprintf(EL_32X, "sh2 sync to %u", m68k_target);
|
||||
|
||||
if (!(Pico32x.regs[0] & P32XS_nRES)) {
|
||||
msh2.m68krcycles_done = ssh2.m68krcycles_done = m68k_target;
|
||||
return; // rare
|
||||
}
|
||||
|
||||
now = msh2.m68krcycles_done;
|
||||
if (CYCLES_GT(now, ssh2.m68krcycles_done))
|
||||
now = ssh2.m68krcycles_done;
|
||||
timer_cycles = now;
|
||||
|
||||
while (CYCLES_GT(m68k_target, now))
|
||||
{
|
||||
if (event_time_next && CYCLES_GE(now, event_time_next))
|
||||
p32x_run_events(now);
|
||||
|
||||
target = m68k_target;
|
||||
if (event_time_next && CYCLES_GT(target, event_time_next))
|
||||
target = event_time_next;
|
||||
|
||||
while (CYCLES_GT(target, now))
|
||||
{
|
||||
elprintf(EL_32X, "sh2 exec to %u %d,%d/%d, flags %x", target,
|
||||
target - msh2.m68krcycles_done, target - ssh2.m68krcycles_done,
|
||||
m68k_target - now, Pico32x.emu_flags);
|
||||
|
||||
if (!(ssh2.state & SH2_IDLE_STATES)) {
|
||||
cycles = target - ssh2.m68krcycles_done;
|
||||
if (cycles > 0) {
|
||||
run_sh2(&ssh2, cycles);
|
||||
|
||||
if (event_time_next && CYCLES_GT(target, event_time_next))
|
||||
target = event_time_next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(msh2.state & SH2_IDLE_STATES)) {
|
||||
cycles = target - msh2.m68krcycles_done;
|
||||
if (cycles > 0) {
|
||||
run_sh2(&msh2, cycles);
|
||||
|
||||
if (event_time_next && CYCLES_GT(target, event_time_next))
|
||||
target = event_time_next;
|
||||
}
|
||||
}
|
||||
|
||||
now = target;
|
||||
if (!(msh2.state & SH2_IDLE_STATES)) {
|
||||
if (CYCLES_GT(now, msh2.m68krcycles_done))
|
||||
now = msh2.m68krcycles_done;
|
||||
}
|
||||
if (!(ssh2.state & SH2_IDLE_STATES)) {
|
||||
if (CYCLES_GT(now, ssh2.m68krcycles_done))
|
||||
now = ssh2.m68krcycles_done;
|
||||
}
|
||||
}
|
||||
|
||||
p32x_timers_do(now - timer_cycles);
|
||||
timer_cycles = now;
|
||||
}
|
||||
|
||||
// advance idle CPUs
|
||||
if (msh2.state & SH2_IDLE_STATES) {
|
||||
if (CYCLES_GT(m68k_target, msh2.m68krcycles_done))
|
||||
msh2.m68krcycles_done = m68k_target;
|
||||
}
|
||||
if (ssh2.state & SH2_IDLE_STATES) {
|
||||
if (CYCLES_GT(m68k_target, ssh2.m68krcycles_done))
|
||||
ssh2.m68krcycles_done = m68k_target;
|
||||
}
|
||||
}
|
||||
|
||||
#define STEP_68K 24
|
||||
|
||||
void sync_sh2s_lockstep(unsigned int m68k_target)
|
||||
{
|
||||
unsigned int mcycles;
|
||||
|
||||
mcycles = msh2.m68krcycles_done;
|
||||
if (ssh2.m68krcycles_done < mcycles)
|
||||
mcycles = ssh2.m68krcycles_done;
|
||||
|
||||
while (mcycles < m68k_target) {
|
||||
mcycles += STEP_68K;
|
||||
sync_sh2s_normal(mcycles);
|
||||
}
|
||||
}
|
||||
|
||||
#define CPUS_RUN(m68k_cycles) do { \
|
||||
if (PicoAHW & PAHW_MCD) \
|
||||
pcd_run_cpus(m68k_cycles); \
|
||||
else \
|
||||
SekRunM68k(m68k_cycles); \
|
||||
\
|
||||
if ((Pico32x.emu_flags & P32XF_Z80_32X_IO) && Pico.m.z80Run \
|
||||
&& !Pico.m.z80_reset && (PicoOpt & POPT_EN_Z80)) \
|
||||
PicoSyncZ80(SekCyclesDone()); \
|
||||
if (Pico32x.emu_flags & (P32XF_68KCPOLL|P32XF_68KVPOLL)) \
|
||||
p32x_sync_sh2s(SekCyclesDone()); \
|
||||
} while (0)
|
||||
|
||||
#define PICO_32X
|
||||
#define PICO_CD
|
||||
#include "../pico_cmn.inc"
|
||||
|
||||
void PicoFrame32x(void)
|
||||
{
|
||||
Pico.m.scanline = 0;
|
||||
|
||||
Pico32x.vdp_regs[0x0a/2] &= ~P32XV_VBLK; // get out of vblank
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) != 0) // no forced blanking
|
||||
Pico32x.vdp_regs[0x0a/2] &= ~P32XV_PEN; // no palette access
|
||||
|
||||
if (!(Pico32x.sh2_regs[0] & 0x80))
|
||||
p32x_schedule_hint(NULL, SekCyclesDone());
|
||||
p32x_sh2_poll_event(&msh2, SH2_STATE_VPOLL, 0);
|
||||
p32x_sh2_poll_event(&ssh2, SH2_STATE_VPOLL, 0);
|
||||
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
pcd_prepare_frame();
|
||||
|
||||
PicoFrameStart();
|
||||
PicoFrameHints();
|
||||
|
||||
elprintf(EL_32X, "poll: %02x %02x %02x",
|
||||
Pico32x.emu_flags & 3, msh2.state, ssh2.state);
|
||||
}
|
||||
|
||||
// calculate multipliers against 68k clock (7670442)
|
||||
// normally * 3, but effectively slower due to high latencies everywhere
|
||||
// however using something lower breaks MK2 animations
|
||||
void Pico32xSetClocks(int msh2_hz, int ssh2_hz)
|
||||
{
|
||||
float m68k_clk = (float)(OSC_NTSC / 7);
|
||||
if (msh2_hz > 0) {
|
||||
msh2.mult_m68k_to_sh2 = (int)((float)msh2_hz * (1 << CYCLE_MULT_SHIFT) / m68k_clk);
|
||||
msh2.mult_sh2_to_m68k = (int)(m68k_clk * (1 << CYCLE_MULT_SHIFT) / (float)msh2_hz);
|
||||
}
|
||||
if (ssh2_hz > 0) {
|
||||
ssh2.mult_m68k_to_sh2 = (int)((float)ssh2_hz * (1 << CYCLE_MULT_SHIFT) / m68k_clk);
|
||||
ssh2.mult_sh2_to_m68k = (int)(m68k_clk * (1 << CYCLE_MULT_SHIFT) / (float)ssh2_hz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2009,2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "../pico_int.h"
|
||||
|
||||
int (*PicoScan32xBegin)(unsigned int num);
|
||||
int (*PicoScan32xEnd)(unsigned int num);
|
||||
int Pico32xDrawMode;
|
||||
|
||||
static void convert_pal555(int invert_prio)
|
||||
{
|
||||
unsigned int *ps = (void *)Pico32xMem->pal;
|
||||
unsigned int *pd = (void *)Pico32xMem->pal_native;
|
||||
unsigned int m1 = 0x001f001f;
|
||||
unsigned int m2 = 0x03e003e0;
|
||||
unsigned int m3 = 0xfc00fc00;
|
||||
unsigned int inv = 0;
|
||||
int i;
|
||||
|
||||
if (invert_prio)
|
||||
inv = 0x00200020;
|
||||
|
||||
// place prio to LS green bit
|
||||
for (i = 0x100/2; i > 0; i--, ps++, pd++) {
|
||||
unsigned int t = *ps;
|
||||
*pd = (((t & m1) << 11) | ((t & m2) << 1) | ((t & m3) >> 10)) ^ inv;
|
||||
}
|
||||
|
||||
Pico32x.dirty_pal = 0;
|
||||
}
|
||||
|
||||
// direct color mode
|
||||
#define do_line_dc(pd, p32x, pmd, inv, pmd_draw_code) \
|
||||
{ \
|
||||
const unsigned int m1 = 0x001f; \
|
||||
const unsigned int m2 = 0x03e0; \
|
||||
const unsigned int m3 = 0x7c00; \
|
||||
int i; \
|
||||
\
|
||||
for (i = 320; i > 0; i--, pd++, p32x++, pmd++) { \
|
||||
unsigned short t = *p32x; \
|
||||
if ((*pmd & 0x3f) != mdbg && !((t ^ inv) & 0x8000)) { \
|
||||
pmd_draw_code; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
*pd = ((t & m1) << 11) | ((t & m2) << 1) | ((t & m3) >> 10); \
|
||||
} \
|
||||
}
|
||||
|
||||
// packed pixel mode
|
||||
#define do_line_pp(pd, p32x, pmd, pmd_draw_code) \
|
||||
{ \
|
||||
unsigned short t; \
|
||||
int i; \
|
||||
for (i = 320; i > 0; i--, pd++, p32x++, pmd++) { \
|
||||
t = pal[*(unsigned char *)((long)p32x ^ 1)]; \
|
||||
if ((t & 0x20) || (*pmd & 0x3f) == mdbg) \
|
||||
*pd = t; \
|
||||
else \
|
||||
pmd_draw_code; \
|
||||
} \
|
||||
}
|
||||
|
||||
// run length mode
|
||||
#define do_line_rl(pd, p32x, pmd, pmd_draw_code) \
|
||||
{ \
|
||||
unsigned short len, t; \
|
||||
int i; \
|
||||
for (i = 320; i > 0; p32x++) { \
|
||||
t = pal[*p32x & 0xff]; \
|
||||
for (len = (*p32x >> 8) + 1; len > 0 && i > 0; len--, i--, pd++, pmd++) { \
|
||||
if ((*pmd & 0x3f) == mdbg || (t & 0x20)) \
|
||||
*pd = t; \
|
||||
else \
|
||||
pmd_draw_code; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
// this is almost never used (Wiz and menu bg gen only)
|
||||
void FinalizeLine32xRGB555(int sh, int line)
|
||||
{
|
||||
unsigned short *pd = DrawLineDest;
|
||||
unsigned short *pal = Pico32xMem->pal_native;
|
||||
unsigned char *pmd = HighCol + 8;
|
||||
unsigned short *dram, *p32x;
|
||||
unsigned char mdbg;
|
||||
|
||||
FinalizeLine555(sh, line);
|
||||
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 0 || // 32x blanking
|
||||
// XXX: how is 32col mode hadled by real hardware?
|
||||
!(Pico.video.reg[12] & 1) || // 32col mode
|
||||
!(PicoDrawMask & PDRAW_32X_ON))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dram = (void *)Pico32xMem->dram[Pico32x.vdp_regs[0x0a/2] & P32XV_FS];
|
||||
p32x = dram + dram[line];
|
||||
mdbg = Pico.video.reg[7] & 0x3f;
|
||||
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 2) { // Direct Color Mode
|
||||
int inv_bit = (Pico32x.vdp_regs[0] & P32XV_PRI) ? 0x8000 : 0;
|
||||
do_line_dc(pd, p32x, pmd, inv_bit,);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Pico32x.dirty_pal)
|
||||
convert_pal555(Pico32x.vdp_regs[0] & P32XV_PRI);
|
||||
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 1) { // Packed Pixel Mode
|
||||
unsigned char *p32xb = (void *)p32x;
|
||||
if (Pico32x.vdp_regs[2 / 2] & P32XV_SFT)
|
||||
p32xb++;
|
||||
do_line_pp(pd, p32xb, pmd,);
|
||||
}
|
||||
else { // Run Length Mode
|
||||
do_line_rl(pd, p32x, pmd,);
|
||||
}
|
||||
}
|
||||
|
||||
#define MD_LAYER_CODE \
|
||||
*dst = palmd[*pmd]
|
||||
|
||||
#define PICOSCAN_PRE \
|
||||
PicoScan32xBegin(l + (lines_sft_offs & 0xff)); \
|
||||
dst = DrawLineDest; \
|
||||
|
||||
#define PICOSCAN_POST \
|
||||
PicoScan32xEnd(l + (lines_sft_offs & 0xff)); \
|
||||
|
||||
#define make_do_loop(name, pre_code, post_code, md_code) \
|
||||
/* Direct Color Mode */ \
|
||||
static void do_loop_dc##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_sft_offs, int mdbg) \
|
||||
{ \
|
||||
int inv_bit = (Pico32x.vdp_regs[0] & P32XV_PRI) ? 0x8000 : 0; \
|
||||
unsigned char *pmd = PicoDraw2FB + \
|
||||
328 * (lines_sft_offs & 0xff) + 8; \
|
||||
unsigned short *palmd = HighPal; \
|
||||
unsigned short *p32x; \
|
||||
int lines = lines_sft_offs >> 16; \
|
||||
int l; \
|
||||
(void)palmd; \
|
||||
for (l = 0; l < lines; l++, pmd += 8) { \
|
||||
pre_code; \
|
||||
p32x = dram + dram[l]; \
|
||||
do_line_dc(dst, p32x, pmd, inv_bit, md_code); \
|
||||
post_code; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Packed Pixel Mode */ \
|
||||
static void do_loop_pp##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_sft_offs, int mdbg) \
|
||||
{ \
|
||||
unsigned short *pal = Pico32xMem->pal_native; \
|
||||
unsigned char *pmd = PicoDraw2FB + \
|
||||
328 * (lines_sft_offs & 0xff) + 8; \
|
||||
unsigned short *palmd = HighPal; \
|
||||
unsigned char *p32x; \
|
||||
int lines = lines_sft_offs >> 16; \
|
||||
int l; \
|
||||
(void)palmd; \
|
||||
for (l = 0; l < lines; l++, pmd += 8) { \
|
||||
pre_code; \
|
||||
p32x = (void *)(dram + dram[l]); \
|
||||
p32x += (lines_sft_offs >> 8) & 1; \
|
||||
do_line_pp(dst, p32x, pmd, md_code); \
|
||||
post_code; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Run Length Mode */ \
|
||||
static void do_loop_rl##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_sft_offs, int mdbg) \
|
||||
{ \
|
||||
unsigned short *pal = Pico32xMem->pal_native; \
|
||||
unsigned char *pmd = PicoDraw2FB + \
|
||||
328 * (lines_sft_offs & 0xff) + 8; \
|
||||
unsigned short *palmd = HighPal; \
|
||||
unsigned short *p32x; \
|
||||
int lines = lines_sft_offs >> 16; \
|
||||
int l; \
|
||||
(void)palmd; \
|
||||
for (l = 0; l < lines; l++, pmd += 8) { \
|
||||
pre_code; \
|
||||
p32x = dram + dram[l]; \
|
||||
do_line_rl(dst, p32x, pmd, md_code); \
|
||||
post_code; \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef _ASM_32X_DRAW
|
||||
#undef make_do_loop
|
||||
#define make_do_loop(name, pre_code, post_code, md_code) \
|
||||
extern void do_loop_dc##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_offs, int mdbg); \
|
||||
extern void do_loop_pp##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_offs, int mdbg); \
|
||||
extern void do_loop_rl##name(unsigned short *dst, \
|
||||
unsigned short *dram, int lines_offs, int mdbg);
|
||||
#endif
|
||||
|
||||
make_do_loop(,,,)
|
||||
make_do_loop(_md, , , MD_LAYER_CODE)
|
||||
make_do_loop(_scan, PICOSCAN_PRE, PICOSCAN_POST, )
|
||||
make_do_loop(_scan_md, PICOSCAN_PRE, PICOSCAN_POST, MD_LAYER_CODE)
|
||||
|
||||
typedef void (*do_loop_func)(unsigned short *dst, unsigned short *dram, int lines, int mdbg);
|
||||
enum { DO_LOOP, DO_LOOP_MD, DO_LOOP_SCAN, DO_LOOP_MD_SCAN };
|
||||
|
||||
static const do_loop_func do_loop_dc_f[] = { do_loop_dc, do_loop_dc_md, do_loop_dc_scan, do_loop_dc_scan_md };
|
||||
static const do_loop_func do_loop_pp_f[] = { do_loop_pp, do_loop_pp_md, do_loop_pp_scan, do_loop_pp_scan_md };
|
||||
static const do_loop_func do_loop_rl_f[] = { do_loop_rl, do_loop_rl_md, do_loop_rl_scan, do_loop_rl_scan_md };
|
||||
|
||||
void PicoDraw32xLayer(int offs, int lines, int md_bg)
|
||||
{
|
||||
int have_scan = PicoScan32xBegin != NULL && PicoScan32xEnd != NULL;
|
||||
const do_loop_func *do_loop;
|
||||
unsigned short *dram;
|
||||
int lines_sft_offs;
|
||||
int which_func;
|
||||
|
||||
DrawLineDest = (char *)DrawLineDestBase + offs * DrawLineDestIncrement;
|
||||
dram = Pico32xMem->dram[Pico32x.vdp_regs[0x0a/2] & P32XV_FS];
|
||||
|
||||
if (Pico32xDrawMode == PDM32X_BOTH) {
|
||||
if (Pico.m.dirtyPal)
|
||||
PicoDrawUpdateHighPal();
|
||||
}
|
||||
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 2)
|
||||
{
|
||||
// Direct Color Mode
|
||||
do_loop = do_loop_dc_f;
|
||||
goto do_it;
|
||||
}
|
||||
|
||||
if (Pico32x.dirty_pal)
|
||||
convert_pal555(Pico32x.vdp_regs[0] & P32XV_PRI);
|
||||
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 1)
|
||||
{
|
||||
// Packed Pixel Mode
|
||||
do_loop = do_loop_pp_f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run Length Mode
|
||||
do_loop = do_loop_rl_f;
|
||||
}
|
||||
|
||||
do_it:
|
||||
if (Pico32xDrawMode == PDM32X_BOTH)
|
||||
which_func = have_scan ? DO_LOOP_MD_SCAN : DO_LOOP_MD;
|
||||
else
|
||||
which_func = have_scan ? DO_LOOP_SCAN : DO_LOOP;
|
||||
lines_sft_offs = (lines << 16) | offs;
|
||||
if (Pico32x.vdp_regs[2 / 2] & P32XV_SFT)
|
||||
lines_sft_offs |= 1 << 8;
|
||||
|
||||
do_loop[which_func](DrawLineDest, dram, lines_sft_offs, md_bg);
|
||||
}
|
||||
|
||||
// mostly unused, games tend to keep 32X layer on
|
||||
void PicoDraw32xLayerMdOnly(int offs, int lines)
|
||||
{
|
||||
int have_scan = PicoScan32xBegin != NULL && PicoScan32xEnd != NULL;
|
||||
unsigned short *dst = (void *)((char *)DrawLineDestBase + offs * DrawLineDestIncrement);
|
||||
unsigned char *pmd = PicoDraw2FB + 328 * offs + 8;
|
||||
unsigned short *pal = HighPal;
|
||||
int poffs = 0, plen = 320;
|
||||
int l, p;
|
||||
|
||||
if (!(Pico.video.reg[12] & 1)) {
|
||||
// 32col mode
|
||||
poffs = 32;
|
||||
plen = 256;
|
||||
}
|
||||
|
||||
if (Pico.m.dirtyPal)
|
||||
PicoDrawUpdateHighPal();
|
||||
|
||||
dst += poffs;
|
||||
for (l = 0; l < lines; l++) {
|
||||
if (have_scan) {
|
||||
PicoScan32xBegin(l + offs);
|
||||
dst = DrawLineDest + poffs;
|
||||
}
|
||||
for (p = 0; p < plen; p += 4) {
|
||||
dst[p + 0] = pal[*pmd++];
|
||||
dst[p + 1] = pal[*pmd++];
|
||||
dst[p + 2] = pal[*pmd++];
|
||||
dst[p + 3] = pal[*pmd++];
|
||||
}
|
||||
dst = (void *)((char *)dst + DrawLineDestIncrement);
|
||||
pmd += 328 - plen;
|
||||
if (have_scan)
|
||||
PicoScan32xEnd(l + offs);
|
||||
}
|
||||
}
|
||||
|
||||
void PicoDrawSetOutFormat32x(pdso_t which, int use_32x_line_mode)
|
||||
{
|
||||
#ifdef _ASM_32X_DRAW
|
||||
extern void *Pico32xNativePal;
|
||||
Pico32xNativePal = Pico32xMem->pal_native;
|
||||
#endif
|
||||
|
||||
if (which == PDF_RGB555 && use_32x_line_mode) {
|
||||
// we'll draw via FinalizeLine32xRGB555 (rare)
|
||||
PicoDrawSetInternalBuf(NULL, 0);
|
||||
Pico32xDrawMode = PDM32X_OFF;
|
||||
return;
|
||||
}
|
||||
|
||||
// use the same layout as alt renderer
|
||||
PicoDrawSetInternalBuf(PicoDraw2FB, 328);
|
||||
Pico32xDrawMode = (which == PDF_RGB555) ? PDM32X_32X_ONLY : PDM32X_BOTH;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2009,2010,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "../pico_int.h"
|
||||
|
||||
static int pwm_cycles;
|
||||
static int pwm_mult;
|
||||
static int pwm_ptr;
|
||||
static int pwm_irq_reload;
|
||||
static int pwm_doing_fifo;
|
||||
static int pwm_silent;
|
||||
|
||||
void p32x_pwm_ctl_changed(void)
|
||||
{
|
||||
int control = Pico32x.regs[0x30 / 2];
|
||||
int cycles = Pico32x.regs[0x32 / 2];
|
||||
|
||||
cycles = (cycles - 1) & 0x0fff;
|
||||
pwm_cycles = cycles;
|
||||
|
||||
// supposedly we should stop FIFO when xMd is 0,
|
||||
// but mars test disagrees
|
||||
pwm_mult = 0;
|
||||
if ((control & 0x0f) != 0)
|
||||
pwm_mult = 0x10000 / cycles;
|
||||
|
||||
pwm_irq_reload = (control & 0x0f00) >> 8;
|
||||
pwm_irq_reload = ((pwm_irq_reload - 1) & 0x0f) + 1;
|
||||
|
||||
if (Pico32x.pwm_irq_cnt == 0)
|
||||
Pico32x.pwm_irq_cnt = pwm_irq_reload;
|
||||
}
|
||||
|
||||
static void do_pwm_irq(SH2 *sh2, unsigned int m68k_cycles)
|
||||
{
|
||||
p32x_trigger_irq(sh2, m68k_cycles, P32XI_PWM);
|
||||
|
||||
if (Pico32x.regs[0x30 / 2] & P32XP_RTP) {
|
||||
p32x_event_schedule(m68k_cycles, P32X_EVENT_PWM, pwm_cycles / 3 + 1);
|
||||
// note: might recurse
|
||||
p32x_dreq1_trigger();
|
||||
}
|
||||
}
|
||||
|
||||
static int convert_sample(unsigned int v)
|
||||
{
|
||||
if (v == 0)
|
||||
return 0;
|
||||
if (v > pwm_cycles)
|
||||
v = pwm_cycles;
|
||||
return ((int)v - pwm_cycles / 2) * pwm_mult;
|
||||
}
|
||||
|
||||
#define consume_fifo(sh2, m68k_cycles) { \
|
||||
int cycles_diff = ((m68k_cycles) * 3) - Pico32x.pwm_cycle_p; \
|
||||
if (cycles_diff >= pwm_cycles) \
|
||||
consume_fifo_do(sh2, m68k_cycles, cycles_diff); \
|
||||
}
|
||||
|
||||
static void consume_fifo_do(SH2 *sh2, unsigned int m68k_cycles,
|
||||
int sh2_cycles_diff)
|
||||
{
|
||||
struct Pico32xMem *mem = Pico32xMem;
|
||||
unsigned short *fifo_l = mem->pwm_fifo[0];
|
||||
unsigned short *fifo_r = mem->pwm_fifo[1];
|
||||
int sum = 0;
|
||||
|
||||
if (pwm_cycles == 0 || pwm_doing_fifo)
|
||||
return;
|
||||
|
||||
elprintf(EL_PWM, "pwm: %u: consume %d/%d, %d,%d ptr %d",
|
||||
m68k_cycles, sh2_cycles_diff, sh2_cycles_diff / pwm_cycles,
|
||||
Pico32x.pwm_p[0], Pico32x.pwm_p[1], pwm_ptr);
|
||||
|
||||
// this is for recursion from dreq1 writes
|
||||
pwm_doing_fifo = 1;
|
||||
|
||||
for (; sh2_cycles_diff >= pwm_cycles; sh2_cycles_diff -= pwm_cycles)
|
||||
{
|
||||
if (Pico32x.pwm_p[0] > 0) {
|
||||
fifo_l[0] = fifo_l[1];
|
||||
fifo_l[1] = fifo_l[2];
|
||||
fifo_l[2] = fifo_l[3];
|
||||
Pico32x.pwm_p[0]--;
|
||||
mem->pwm_current[0] = convert_sample(fifo_l[0]);
|
||||
sum += mem->pwm_current[0];
|
||||
}
|
||||
if (Pico32x.pwm_p[1] > 0) {
|
||||
fifo_r[0] = fifo_r[1];
|
||||
fifo_r[1] = fifo_r[2];
|
||||
fifo_r[2] = fifo_r[3];
|
||||
Pico32x.pwm_p[1]--;
|
||||
mem->pwm_current[1] = convert_sample(fifo_r[0]);
|
||||
sum += mem->pwm_current[1];
|
||||
}
|
||||
|
||||
mem->pwm[pwm_ptr * 2 ] = mem->pwm_current[0];
|
||||
mem->pwm[pwm_ptr * 2 + 1] = mem->pwm_current[1];
|
||||
pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1);
|
||||
|
||||
if (--Pico32x.pwm_irq_cnt == 0) {
|
||||
Pico32x.pwm_irq_cnt = pwm_irq_reload;
|
||||
do_pwm_irq(sh2, m68k_cycles);
|
||||
}
|
||||
}
|
||||
Pico32x.pwm_cycle_p = m68k_cycles * 3 - sh2_cycles_diff;
|
||||
pwm_doing_fifo = 0;
|
||||
if (sum != 0)
|
||||
pwm_silent = 0;
|
||||
}
|
||||
|
||||
static int p32x_pwm_schedule_(SH2 *sh2, unsigned int m68k_now)
|
||||
{
|
||||
unsigned int sh2_now = m68k_now * 3;
|
||||
int cycles_diff_sh2;
|
||||
|
||||
if (pwm_cycles == 0)
|
||||
return 0;
|
||||
|
||||
cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p;
|
||||
if (cycles_diff_sh2 >= pwm_cycles)
|
||||
consume_fifo_do(sh2, m68k_now, cycles_diff_sh2);
|
||||
|
||||
if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 1))
|
||||
return 0; // masked by everyone
|
||||
|
||||
cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p;
|
||||
return (Pico32x.pwm_irq_cnt * pwm_cycles
|
||||
- cycles_diff_sh2) / 3 + 1;
|
||||
}
|
||||
|
||||
void p32x_pwm_schedule(unsigned int m68k_now)
|
||||
{
|
||||
int after = p32x_pwm_schedule_(NULL, m68k_now);
|
||||
if (after != 0)
|
||||
p32x_event_schedule(m68k_now, P32X_EVENT_PWM, after);
|
||||
}
|
||||
|
||||
void p32x_pwm_schedule_sh2(SH2 *sh2)
|
||||
{
|
||||
int after = p32x_pwm_schedule_(sh2, sh2_cycles_done_m68k(sh2));
|
||||
if (after != 0)
|
||||
p32x_event_schedule_sh2(sh2, P32X_EVENT_PWM, after);
|
||||
}
|
||||
|
||||
void p32x_pwm_sync_to_sh2(SH2 *sh2)
|
||||
{
|
||||
int m68k_cycles = sh2_cycles_done_m68k(sh2);
|
||||
consume_fifo(sh2, m68k_cycles);
|
||||
}
|
||||
|
||||
void p32x_pwm_irq_event(unsigned int m68k_now)
|
||||
{
|
||||
p32x_pwm_schedule(m68k_now);
|
||||
}
|
||||
|
||||
unsigned int p32x_pwm_read16(unsigned int a, SH2 *sh2,
|
||||
unsigned int m68k_cycles)
|
||||
{
|
||||
unsigned int d = 0;
|
||||
|
||||
consume_fifo(sh2, m68k_cycles);
|
||||
|
||||
a &= 0x0e;
|
||||
switch (a) {
|
||||
case 0: // control
|
||||
case 2: // cycle
|
||||
d = Pico32x.regs[(0x30 + a) / 2];
|
||||
break;
|
||||
|
||||
case 4: // L ch
|
||||
if (Pico32x.pwm_p[0] == 3)
|
||||
d |= P32XP_FULL;
|
||||
else if (Pico32x.pwm_p[0] == 0)
|
||||
d |= P32XP_EMPTY;
|
||||
break;
|
||||
|
||||
case 6: // R ch
|
||||
case 8: // MONO
|
||||
if (Pico32x.pwm_p[1] == 3)
|
||||
d |= P32XP_FULL;
|
||||
else if (Pico32x.pwm_p[1] == 0)
|
||||
d |= P32XP_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
elprintf(EL_PWM, "pwm: %u: r16 %02x %04x (p %d %d)",
|
||||
m68k_cycles, a, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]);
|
||||
return d;
|
||||
}
|
||||
|
||||
void p32x_pwm_write16(unsigned int a, unsigned int d,
|
||||
SH2 *sh2, unsigned int m68k_cycles)
|
||||
{
|
||||
elprintf(EL_PWM, "pwm: %u: w16 %02x %04x (p %d %d)",
|
||||
m68k_cycles, a & 0x0e, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]);
|
||||
|
||||
consume_fifo(sh2, m68k_cycles);
|
||||
|
||||
a &= 0x0e;
|
||||
if (a == 0) { // control
|
||||
// avoiding pops..
|
||||
if ((Pico32x.regs[0x30 / 2] & 0x0f) == 0)
|
||||
Pico32xMem->pwm_fifo[0][0] = Pico32xMem->pwm_fifo[1][0] = 0;
|
||||
Pico32x.regs[0x30 / 2] = d;
|
||||
p32x_pwm_ctl_changed();
|
||||
Pico32x.pwm_irq_cnt = pwm_irq_reload; // ?
|
||||
}
|
||||
else if (a == 2) { // cycle
|
||||
Pico32x.regs[0x32 / 2] = d & 0x0fff;
|
||||
p32x_pwm_ctl_changed();
|
||||
}
|
||||
else if (a <= 8) {
|
||||
d = (d - 1) & 0x0fff;
|
||||
|
||||
if (a == 4 || a == 8) { // L ch or MONO
|
||||
unsigned short *fifo = Pico32xMem->pwm_fifo[0];
|
||||
if (Pico32x.pwm_p[0] < 3)
|
||||
Pico32x.pwm_p[0]++;
|
||||
else {
|
||||
fifo[1] = fifo[2];
|
||||
fifo[2] = fifo[3];
|
||||
}
|
||||
fifo[Pico32x.pwm_p[0]] = d;
|
||||
}
|
||||
if (a == 6 || a == 8) { // R ch or MONO
|
||||
unsigned short *fifo = Pico32xMem->pwm_fifo[1];
|
||||
if (Pico32x.pwm_p[1] < 3)
|
||||
Pico32x.pwm_p[1]++;
|
||||
else {
|
||||
fifo[1] = fifo[2];
|
||||
fifo[2] = fifo[3];
|
||||
}
|
||||
fifo[Pico32x.pwm_p[1]] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void p32x_pwm_update(int *buf32, int length, int stereo)
|
||||
{
|
||||
short *pwmb;
|
||||
int step;
|
||||
int p = 0;
|
||||
int xmd;
|
||||
|
||||
consume_fifo(NULL, SekCyclesDone());
|
||||
|
||||
xmd = Pico32x.regs[0x30 / 2] & 0x0f;
|
||||
if (xmd == 0 || xmd == 0x06 || xmd == 0x09 || xmd == 0x0f)
|
||||
goto out; // invalid?
|
||||
if (pwm_silent)
|
||||
return;
|
||||
|
||||
step = (pwm_ptr << 16) / length;
|
||||
pwmb = Pico32xMem->pwm;
|
||||
|
||||
if (stereo)
|
||||
{
|
||||
if (xmd == 0x05) {
|
||||
// normal
|
||||
while (length-- > 0) {
|
||||
*buf32++ += pwmb[0];
|
||||
*buf32++ += pwmb[1];
|
||||
|
||||
p += step;
|
||||
pwmb += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
}
|
||||
}
|
||||
else if (xmd == 0x0a) {
|
||||
// channel swap
|
||||
while (length-- > 0) {
|
||||
*buf32++ += pwmb[1];
|
||||
*buf32++ += pwmb[0];
|
||||
|
||||
p += step;
|
||||
pwmb += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// mono - LMD, RMD specify dst
|
||||
if (xmd & 0x06) // src is R
|
||||
pwmb++;
|
||||
if (xmd & 0x0c) // dst is R
|
||||
buf32++;
|
||||
while (length-- > 0) {
|
||||
*buf32 += *pwmb;
|
||||
|
||||
p += step;
|
||||
pwmb += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
buf32 += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// mostly unused
|
||||
while (length-- > 0) {
|
||||
*buf32++ += pwmb[0];
|
||||
|
||||
p += step;
|
||||
pwmb += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d",
|
||||
pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2);
|
||||
|
||||
out:
|
||||
pwm_ptr = 0;
|
||||
pwm_silent = Pico32xMem->pwm_current[0] == 0
|
||||
&& Pico32xMem->pwm_current[1] == 0;
|
||||
}
|
||||
|
||||
void p32x_pwm_state_loaded(void)
|
||||
{
|
||||
int cycles_diff_sh2;
|
||||
|
||||
p32x_pwm_ctl_changed();
|
||||
|
||||
// for old savestates
|
||||
cycles_diff_sh2 = SekCycleCnt * 3 - Pico32x.pwm_cycle_p;
|
||||
if (cycles_diff_sh2 >= pwm_cycles || cycles_diff_sh2 < 0) {
|
||||
Pico32x.pwm_irq_cnt = pwm_irq_reload;
|
||||
Pico32x.pwm_cycle_p = SekCycleCnt * 3;
|
||||
p32x_pwm_schedule(SekCycleCnt);
|
||||
}
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* SH2 peripherals/"system on chip"
|
||||
* (C) notaz, 2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*
|
||||
* rough fffffe00-ffffffff map:
|
||||
* e00-e05 SCI serial communication interface
|
||||
* e10-e1a FRT free-running timer
|
||||
* e60-e68 VCRx irq vectors
|
||||
* e71-e72 DRCR dma selection
|
||||
* e80-e83 WDT watchdog timer
|
||||
* e91 SBYCR standby control
|
||||
* e92 CCR cache control
|
||||
* ee0 ICR irq control
|
||||
* ee2 IPRA irq priorities
|
||||
* ee4 VCRWDT WDT irq vectors
|
||||
* f00-f17 DIVU
|
||||
* f40-f7b UBC user break controller
|
||||
* f80-fb3 DMAC
|
||||
* fe0-ffb BSC bus state controller
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
#include "../memory.h"
|
||||
|
||||
// DMAC handling
|
||||
struct dma_chan {
|
||||
unsigned int sar, dar; // src, dst addr
|
||||
unsigned int tcr; // transfer count
|
||||
unsigned int chcr; // chan ctl
|
||||
// -- dm dm sm sm ts ts ar am al ds dl tb ta ie te de
|
||||
// ts - transfer size: 1, 2, 4, 16 bytes
|
||||
// ar - auto request if 1, else dreq signal
|
||||
// ie - irq enable
|
||||
// te - transfer end
|
||||
// de - dma enable
|
||||
#define DMA_AR (1 << 9)
|
||||
#define DMA_IE (1 << 2)
|
||||
#define DMA_TE (1 << 1)
|
||||
#define DMA_DE (1 << 0)
|
||||
};
|
||||
|
||||
struct dmac {
|
||||
struct dma_chan chan[2];
|
||||
unsigned int vcrdma0;
|
||||
unsigned int unknown0;
|
||||
unsigned int vcrdma1;
|
||||
unsigned int unknown1;
|
||||
unsigned int dmaor;
|
||||
// -- pr ae nmif dme
|
||||
// pr - priority: chan0 > chan1 or round-robin
|
||||
// ae - address error
|
||||
// nmif - nmi occurred
|
||||
// dme - DMA master enable
|
||||
#define DMA_DME (1 << 0)
|
||||
};
|
||||
|
||||
static void dmac_te_irq(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
char *regs = (void *)sh2->peri_regs;
|
||||
struct dmac *dmac = (void *)(regs + 0x180);
|
||||
int level = PREG8(regs, 0xe2) & 0x0f; // IPRA
|
||||
int vector = (chan == &dmac->chan[0]) ?
|
||||
dmac->vcrdma0 : dmac->vcrdma1;
|
||||
|
||||
elprintf(EL_32XP, "dmac irq %d %d", level, vector);
|
||||
sh2_internal_irq(sh2, level, vector & 0x7f);
|
||||
}
|
||||
|
||||
static void dmac_transfer_complete(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
chan->chcr |= DMA_TE; // DMA has ended normally
|
||||
|
||||
p32x_sh2_poll_event(sh2, SH2_STATE_SLEEP, SekCyclesDone());
|
||||
if (chan->chcr & DMA_IE)
|
||||
dmac_te_irq(sh2, chan);
|
||||
}
|
||||
|
||||
static void dmac_transfer_one(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
u32 size, d;
|
||||
|
||||
size = (chan->chcr >> 10) & 3;
|
||||
switch (size) {
|
||||
case 0:
|
||||
d = p32x_sh2_read8(chan->sar, sh2);
|
||||
p32x_sh2_write8(chan->dar, d, sh2);
|
||||
case 1:
|
||||
d = p32x_sh2_read16(chan->sar, sh2);
|
||||
p32x_sh2_write16(chan->dar, d, sh2);
|
||||
break;
|
||||
case 2:
|
||||
d = p32x_sh2_read32(chan->sar, sh2);
|
||||
p32x_sh2_write32(chan->dar, d, sh2);
|
||||
break;
|
||||
case 3:
|
||||
d = p32x_sh2_read32(chan->sar + 0x00, sh2);
|
||||
p32x_sh2_write32(chan->dar + 0x00, d, sh2);
|
||||
d = p32x_sh2_read32(chan->sar + 0x04, sh2);
|
||||
p32x_sh2_write32(chan->dar + 0x04, d, sh2);
|
||||
d = p32x_sh2_read32(chan->sar + 0x08, sh2);
|
||||
p32x_sh2_write32(chan->dar + 0x08, d, sh2);
|
||||
d = p32x_sh2_read32(chan->sar + 0x0c, sh2);
|
||||
p32x_sh2_write32(chan->dar + 0x0c, d, sh2);
|
||||
chan->sar += 16; // always?
|
||||
if (chan->chcr & (1 << 15))
|
||||
chan->dar -= 16;
|
||||
if (chan->chcr & (1 << 14))
|
||||
chan->dar += 16;
|
||||
chan->tcr -= 4;
|
||||
return;
|
||||
}
|
||||
chan->tcr--;
|
||||
|
||||
size = 1 << size;
|
||||
if (chan->chcr & (1 << 15))
|
||||
chan->dar -= size;
|
||||
if (chan->chcr & (1 << 14))
|
||||
chan->dar += size;
|
||||
if (chan->chcr & (1 << 13))
|
||||
chan->sar -= size;
|
||||
if (chan->chcr & (1 << 12))
|
||||
chan->sar += size;
|
||||
}
|
||||
|
||||
// DMA trigger by SH2 register write
|
||||
static void dmac_trigger(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
elprintf_sh2(sh2, EL_32XP, "DMA %08x->%08x, cnt %d, chcr %04x @%06x",
|
||||
chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc);
|
||||
chan->tcr &= 0xffffff;
|
||||
|
||||
if (chan->chcr & DMA_AR) {
|
||||
// auto-request transfer
|
||||
while ((int)chan->tcr > 0)
|
||||
dmac_transfer_one(sh2, chan);
|
||||
dmac_transfer_complete(sh2, chan);
|
||||
return;
|
||||
}
|
||||
|
||||
// DREQ0 is only sent after first 4 words are written.
|
||||
// we do multiple of 4 words to avoid messing up alignment
|
||||
if ((chan->sar & ~0x20000000) == 0x00004012) {
|
||||
if (Pico32x.dmac0_fifo_ptr && (Pico32x.dmac0_fifo_ptr & 3) == 0) {
|
||||
elprintf(EL_32XP, "68k -> sh2 DMA");
|
||||
p32x_dreq0_trigger();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// DREQ1
|
||||
if ((chan->dar & 0xc7fffff0) == 0x00004030)
|
||||
return;
|
||||
|
||||
elprintf(EL_32XP|EL_ANOMALY, "unhandled DMA: "
|
||||
"%08x->%08x, cnt %d, chcr %04x @%06x",
|
||||
chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc);
|
||||
}
|
||||
|
||||
// timer state - FIXME
|
||||
static int timer_cycles[2];
|
||||
static int timer_tick_cycles[2];
|
||||
|
||||
// timers
|
||||
void p32x_timers_recalc(void)
|
||||
{
|
||||
int cycles;
|
||||
int tmp, i;
|
||||
|
||||
// SH2 timer step
|
||||
for (i = 0; i < 2; i++) {
|
||||
tmp = PREG8(sh2s[i].peri_regs, 0x80) & 7;
|
||||
// Sclk cycles per timer tick
|
||||
if (tmp)
|
||||
cycles = 0x20 << tmp;
|
||||
else
|
||||
cycles = 2;
|
||||
timer_tick_cycles[i] = cycles;
|
||||
timer_cycles[i] = 0;
|
||||
elprintf(EL_32XP, "WDT cycles[%d] = %d", i, cycles);
|
||||
}
|
||||
}
|
||||
|
||||
void p32x_timers_do(unsigned int m68k_slice)
|
||||
{
|
||||
unsigned int cycles = m68k_slice * 3;
|
||||
int cnt, i;
|
||||
|
||||
// WDT timers
|
||||
for (i = 0; i < 2; i++) {
|
||||
void *pregs = sh2s[i].peri_regs;
|
||||
if (PREG8(pregs, 0x80) & 0x20) { // TME
|
||||
timer_cycles[i] += cycles;
|
||||
cnt = PREG8(pregs, 0x81);
|
||||
while (timer_cycles[i] >= timer_tick_cycles[i]) {
|
||||
timer_cycles[i] -= timer_tick_cycles[i];
|
||||
cnt++;
|
||||
}
|
||||
if (cnt >= 0x100) {
|
||||
int level = PREG8(pregs, 0xe3) >> 4;
|
||||
int vector = PREG8(pregs, 0xe4) & 0x7f;
|
||||
elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)",
|
||||
i ? 's' : 'm', level, vector);
|
||||
sh2_internal_irq(&sh2s[i], level, vector);
|
||||
cnt &= 0xff;
|
||||
}
|
||||
PREG8(pregs, 0x81) = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sh2_peripheral_reset(SH2 *sh2)
|
||||
{
|
||||
memset(sh2->peri_regs, 0, sizeof(sh2->peri_regs)); // ?
|
||||
PREG8(sh2->peri_regs, 0x001) = 0xff; // SCI BRR
|
||||
PREG8(sh2->peri_regs, 0x003) = 0xff; // SCI TDR
|
||||
PREG8(sh2->peri_regs, 0x004) = 0x84; // SCI SSR
|
||||
PREG8(sh2->peri_regs, 0x011) = 0x01; // TIER
|
||||
PREG8(sh2->peri_regs, 0x017) = 0xe0; // TOCR
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// SH2 internal peripheral memhandlers
|
||||
// we keep them in little endian format
|
||||
|
||||
u32 sh2_peripheral_read8(u32 a, SH2 *sh2)
|
||||
{
|
||||
u8 *r = (void *)sh2->peri_regs;
|
||||
u32 d;
|
||||
|
||||
a &= 0x1ff;
|
||||
d = PREG8(r, a);
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r8 [%08x] %02x @%06x",
|
||||
a | ~0x1ff, d, sh2_pc(sh2));
|
||||
return d;
|
||||
}
|
||||
|
||||
u32 sh2_peripheral_read16(u32 a, SH2 *sh2)
|
||||
{
|
||||
u16 *r = (void *)sh2->peri_regs;
|
||||
u32 d;
|
||||
|
||||
a &= 0x1ff;
|
||||
d = r[(a / 2) ^ 1];
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r16 [%08x] %04x @%06x",
|
||||
a | ~0x1ff, d, sh2_pc(sh2));
|
||||
return d;
|
||||
}
|
||||
|
||||
u32 sh2_peripheral_read32(u32 a, SH2 *sh2)
|
||||
{
|
||||
u32 d;
|
||||
a &= 0x1fc;
|
||||
d = sh2->peri_regs[a / 4];
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r32 [%08x] %08x @%06x",
|
||||
a | ~0x1ff, d, sh2_pc(sh2));
|
||||
return d;
|
||||
}
|
||||
|
||||
static void sci_trigger(SH2 *sh2, u8 *r)
|
||||
{
|
||||
u8 *oregs;
|
||||
|
||||
if (!(PREG8(r, 2) & 0x20))
|
||||
return; // transmitter not enabled
|
||||
if ((PREG8(r, 4) & 0x80)) // TDRE - TransmitDataR Empty
|
||||
return;
|
||||
|
||||
oregs = (u8 *)sh2->other_sh2->peri_regs;
|
||||
if (!(PREG8(oregs, 2) & 0x10))
|
||||
return; // receiver not enabled
|
||||
|
||||
PREG8(oregs, 5) = PREG8(r, 3); // other.RDR = this.TDR
|
||||
PREG8(r, 4) |= 0x80; // TDRE - TDR empty
|
||||
PREG8(oregs, 4) |= 0x40; // RDRF - RDR Full
|
||||
|
||||
// might need to delay these a bit..
|
||||
if (PREG8(r, 2) & 0x80) { // TIE - tx irq enabled
|
||||
int level = PREG8(oregs, 0x60) >> 4;
|
||||
int vector = PREG8(oregs, 0x64) & 0x7f;
|
||||
elprintf_sh2(sh2, EL_32XP, "SCI tx irq (%d, %d)",
|
||||
level, vector);
|
||||
sh2_internal_irq(sh2, level, vector);
|
||||
}
|
||||
// TODO: TEIE
|
||||
if (PREG8(oregs, 2) & 0x40) { // RIE - rx irq enabled
|
||||
int level = PREG8(oregs, 0x60) >> 4;
|
||||
int vector = PREG8(oregs, 0x63) & 0x7f;
|
||||
elprintf_sh2(sh2->other_sh2, EL_32XP, "SCI rx irq (%d, %d)",
|
||||
level, vector);
|
||||
sh2_internal_irq(sh2->other_sh2, level, vector);
|
||||
}
|
||||
}
|
||||
|
||||
void REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, SH2 *sh2)
|
||||
{
|
||||
u8 *r = (void *)sh2->peri_regs;
|
||||
u8 old;
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri w8 [%08x] %02x @%06x",
|
||||
a, d, sh2_pc(sh2));
|
||||
|
||||
a &= 0x1ff;
|
||||
old = PREG8(r, a);
|
||||
|
||||
switch (a) {
|
||||
case 0x002: // SCR - serial control
|
||||
if (!(PREG8(r, a) & 0x20) && (d & 0x20)) { // TE being set
|
||||
PREG8(r, a) = d;
|
||||
sci_trigger(sh2, r);
|
||||
}
|
||||
break;
|
||||
case 0x003: // TDR - transmit data
|
||||
break;
|
||||
case 0x004: // SSR - serial status
|
||||
d = (old & (d | 0x06)) | (d & 1);
|
||||
PREG8(r, a) = d;
|
||||
sci_trigger(sh2, r);
|
||||
return;
|
||||
case 0x005: // RDR - receive data
|
||||
break;
|
||||
case 0x010: // TIER
|
||||
if (d & 0x8e)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "TIER: %02x", d);
|
||||
d = (d & 0x8e) | 1;
|
||||
break;
|
||||
case 0x017: // TOCR
|
||||
d |= 0xe0;
|
||||
break;
|
||||
}
|
||||
PREG8(r, a) = d;
|
||||
}
|
||||
|
||||
void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2)
|
||||
{
|
||||
u16 *r = (void *)sh2->peri_regs;
|
||||
elprintf_sh2(sh2, EL_32XP, "peri w16 [%08x] %04x @%06x",
|
||||
a, d, sh2_pc(sh2));
|
||||
|
||||
a &= 0x1ff;
|
||||
|
||||
// evil WDT
|
||||
if (a == 0x80) {
|
||||
if ((d & 0xff00) == 0xa500) { // WTCSR
|
||||
PREG8(r, 0x80) = d;
|
||||
p32x_timers_recalc();
|
||||
}
|
||||
if ((d & 0xff00) == 0x5a00) // WTCNT
|
||||
PREG8(r, 0x81) = d;
|
||||
return;
|
||||
}
|
||||
|
||||
r[(a / 2) ^ 1] = d;
|
||||
}
|
||||
|
||||
void REGPARM(3) sh2_peripheral_write32(u32 a, u32 d, SH2 *sh2)
|
||||
{
|
||||
u32 *r = sh2->peri_regs;
|
||||
u32 old;
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri w32 [%08x] %08x @%06x",
|
||||
a, d, sh2_pc(sh2));
|
||||
|
||||
a &= 0x1fc;
|
||||
old = r[a / 4];
|
||||
r[a / 4] = d;
|
||||
|
||||
switch (a) {
|
||||
// division unit (TODO: verify):
|
||||
case 0x104: // DVDNT: divident L, starts divide
|
||||
elprintf_sh2(sh2, EL_32XP, "divide %08x / %08x",
|
||||
d, r[0x100 / 4]);
|
||||
if (r[0x100 / 4]) {
|
||||
signed int divisor = r[0x100 / 4];
|
||||
r[0x118 / 4] = r[0x110 / 4] = (signed int)d % divisor;
|
||||
r[0x104 / 4] = r[0x11c / 4] = r[0x114 / 4] = (signed int)d / divisor;
|
||||
}
|
||||
else
|
||||
r[0x110 / 4] = r[0x114 / 4] = r[0x118 / 4] = r[0x11c / 4] = 0; // ?
|
||||
break;
|
||||
case 0x114:
|
||||
elprintf_sh2(sh2, EL_32XP, "divide %08x%08x / %08x @%08x",
|
||||
r[0x110 / 4], d, r[0x100 / 4], sh2_pc(sh2));
|
||||
if (r[0x100 / 4]) {
|
||||
signed long long divident = (signed long long)r[0x110 / 4] << 32 | d;
|
||||
signed int divisor = r[0x100 / 4];
|
||||
// XXX: undocumented mirroring to 0x118,0x11c?
|
||||
r[0x118 / 4] = r[0x110 / 4] = divident % divisor;
|
||||
divident /= divisor;
|
||||
r[0x11c / 4] = r[0x114 / 4] = divident;
|
||||
divident >>= 31;
|
||||
if ((unsigned long long)divident + 1 > 1) {
|
||||
//elprintf_sh2(sh2, EL_32XP, "divide overflow! @%08x", sh2_pc(sh2));
|
||||
r[0x11c / 4] = r[0x114 / 4] = divident > 0 ? 0x7fffffff : 0x80000000; // overflow
|
||||
}
|
||||
}
|
||||
else
|
||||
r[0x110 / 4] = r[0x114 / 4] = r[0x118 / 4] = r[0x11c / 4] = 0; // ?
|
||||
break;
|
||||
}
|
||||
|
||||
// perhaps starting a DMA?
|
||||
if (a == 0x1b0 || a == 0x18c || a == 0x19c) {
|
||||
struct dmac *dmac = (void *)&sh2->peri_regs[0x180 / 4];
|
||||
if (a == 0x1b0 && !((old ^ d) & d & DMA_DME))
|
||||
return;
|
||||
if (!(dmac->dmaor & DMA_DME))
|
||||
return;
|
||||
|
||||
if ((dmac->chan[0].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
|
||||
dmac_trigger(sh2, &dmac->chan[0]);
|
||||
if ((dmac->chan[1].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
|
||||
dmac_trigger(sh2, &dmac->chan[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* 32X specific */
|
||||
static void dreq0_do(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
unsigned short dreqlen = Pico32x.regs[0x10 / 2];
|
||||
int i;
|
||||
|
||||
// debug/sanity checks
|
||||
if (chan->tcr < dreqlen || chan->tcr > dreqlen + 4)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq0: tcr0/len inconsistent: %d/%d",
|
||||
chan->tcr, dreqlen);
|
||||
// note: DACK is not connected, single addr mode should not be used
|
||||
if ((chan->chcr & 0x3f08) != 0x0400)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq0: bad control: %04x", chan->chcr);
|
||||
if ((chan->sar & ~0x20000000) != 0x00004012)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq0: bad sar?: %08x", chan->sar);
|
||||
|
||||
// HACK: assume bus is busy and SH2 is halted
|
||||
sh2->state |= SH2_STATE_SLEEP;
|
||||
|
||||
for (i = 0; i < Pico32x.dmac0_fifo_ptr && chan->tcr > 0; i++) {
|
||||
elprintf_sh2(sh2, EL_32XP, "dreq0 [%08x] %04x, dreq_len %d",
|
||||
chan->dar, Pico32x.dmac_fifo[i], dreqlen);
|
||||
p32x_sh2_write16(chan->dar, Pico32x.dmac_fifo[i], sh2);
|
||||
chan->dar += 2;
|
||||
chan->tcr--;
|
||||
}
|
||||
|
||||
if (Pico32x.dmac0_fifo_ptr != i)
|
||||
memmove(Pico32x.dmac_fifo, &Pico32x.dmac_fifo[i],
|
||||
(Pico32x.dmac0_fifo_ptr - i) * 2);
|
||||
Pico32x.dmac0_fifo_ptr -= i;
|
||||
|
||||
Pico32x.regs[6 / 2] &= ~P32XS_FULL;
|
||||
if (chan->tcr == 0)
|
||||
dmac_transfer_complete(sh2, chan);
|
||||
else
|
||||
sh2_end_run(sh2, 16);
|
||||
}
|
||||
|
||||
static void dreq1_do(SH2 *sh2, struct dma_chan *chan)
|
||||
{
|
||||
// debug/sanity checks
|
||||
if ((chan->chcr & 0xc308) != 0x0000)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq1: bad control: %04x", chan->chcr);
|
||||
if ((chan->dar & ~0xf) != 0x20004030)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq1: bad dar?: %08x\n", chan->dar);
|
||||
|
||||
dmac_transfer_one(sh2, chan);
|
||||
if (chan->tcr == 0)
|
||||
dmac_transfer_complete(sh2, chan);
|
||||
}
|
||||
|
||||
void p32x_dreq0_trigger(void)
|
||||
{
|
||||
struct dmac *mdmac = (void *)&msh2.peri_regs[0x180 / 4];
|
||||
struct dmac *sdmac = (void *)&ssh2.peri_regs[0x180 / 4];
|
||||
|
||||
elprintf(EL_32XP, "dreq0_trigger");
|
||||
if ((mdmac->dmaor & DMA_DME) && (mdmac->chan[0].chcr & 3) == DMA_DE) {
|
||||
dreq0_do(&msh2, &mdmac->chan[0]);
|
||||
}
|
||||
if ((sdmac->dmaor & DMA_DME) && (sdmac->chan[0].chcr & 3) == DMA_DE) {
|
||||
dreq0_do(&ssh2, &sdmac->chan[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void p32x_dreq1_trigger(void)
|
||||
{
|
||||
struct dmac *mdmac = (void *)&msh2.peri_regs[0x180 / 4];
|
||||
struct dmac *sdmac = (void *)&ssh2.peri_regs[0x180 / 4];
|
||||
int hit = 0;
|
||||
|
||||
elprintf(EL_32XP, "dreq1_trigger");
|
||||
if ((mdmac->dmaor & DMA_DME) && (mdmac->chan[1].chcr & 3) == DMA_DE) {
|
||||
dreq1_do(&msh2, &mdmac->chan[1]);
|
||||
hit = 1;
|
||||
}
|
||||
if ((sdmac->dmaor & DMA_DME) && (sdmac->chan[1].chcr & 3) == DMA_DE) {
|
||||
dreq1_do(&ssh2, &sdmac->chan[1]);
|
||||
hit = 1;
|
||||
}
|
||||
|
||||
// debug
|
||||
#if (EL_LOGMASK & (EL_32XP|EL_ANOMALY))
|
||||
{
|
||||
static int miss_count;
|
||||
if (!hit) {
|
||||
if (++miss_count == 4)
|
||||
elprintf(EL_32XP|EL_ANOMALY, "dreq1: nobody cared");
|
||||
}
|
||||
else
|
||||
miss_count = 0;
|
||||
}
|
||||
#endif
|
||||
(void)hit;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,703 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint32_t crc32tab[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
|
||||
uint32_t crc32(const uint8_t *p, size_t size)
|
||||
{
|
||||
uint32_t crc = ~0;
|
||||
while (size--)
|
||||
crc = (crc >> 8) ^ crc32tab[(crc ^ (*p++)) & 0xFF];
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
|
||||
static int rom_alloc_size;
|
||||
static const char *rom_exts[] = { "bin", "gen", "smd", "iso", "sms", "gg", "sg" };
|
||||
|
||||
void (*PicoCartMemSetup)(void);
|
||||
|
||||
void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled in Pico/cd/cd_file.c
|
||||
|
||||
int PicoGameLoaded;
|
||||
|
||||
static void PicoCartDetect(const char *carthw_cfg);
|
||||
|
||||
static const char *get_ext(const char *path)
|
||||
{
|
||||
const char *ext;
|
||||
if (strlen(path) < 4)
|
||||
return ""; // no ext
|
||||
|
||||
// allow 2 or 3 char extensions for now
|
||||
ext = path + strlen(path) - 2;
|
||||
if (ext[-1] != '.') ext--;
|
||||
if (ext[-1] != '.')
|
||||
return "";
|
||||
return ext;
|
||||
}
|
||||
|
||||
// byteswap, data needs to be int aligned, src can match dst
|
||||
void Byteswap(void *dst, const void *src, int len)
|
||||
{
|
||||
const unsigned int *ps = src;
|
||||
unsigned int *pd = dst;
|
||||
int i, m;
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
|
||||
m = 0x00ff00ff;
|
||||
for (i = 0; i < len / 4; i++) {
|
||||
unsigned int t = ps[i];
|
||||
pd[i] = ((t & m) << 8) | ((t & ~m) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Interleve a 16k block and byteswap
|
||||
static int InterleveBlock(unsigned char *dest,unsigned char *src)
|
||||
{
|
||||
int i=0;
|
||||
for (i=0;i<0x2000;i++) dest[(i<<1) ]=src[ i]; // Odd
|
||||
for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decode a SMD file
|
||||
static int DecodeSmd(unsigned char *data,int len)
|
||||
{
|
||||
unsigned char *temp=NULL;
|
||||
int i=0;
|
||||
|
||||
temp=(unsigned char *)malloc(0x4000);
|
||||
if (temp==NULL) return 1;
|
||||
memset(temp,0,0x4000);
|
||||
|
||||
// Interleve each 16k block and shift down by 0x200:
|
||||
for (i=0; i+0x4200<=len; i+=0x4000)
|
||||
{
|
||||
InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer
|
||||
memcpy(data+i,temp,0x4000); // Copy back in
|
||||
}
|
||||
|
||||
free(temp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char *PicoCartAlloc(int filesize, int is_sms)
|
||||
{
|
||||
unsigned char *rom;
|
||||
|
||||
if (is_sms) {
|
||||
// make size power of 2 for easier banking handling
|
||||
int s = 0, tmp = filesize;
|
||||
while ((tmp >>= 1) != 0)
|
||||
s++;
|
||||
if (filesize > (1 << s))
|
||||
s++;
|
||||
rom_alloc_size = 1 << s;
|
||||
// be sure we can cover all address space
|
||||
if (rom_alloc_size < 0x10000)
|
||||
rom_alloc_size = 0x10000;
|
||||
}
|
||||
else {
|
||||
// make alloc size at least sizeof(mcd_state),
|
||||
// in case we want to switch to CD mode
|
||||
if (filesize < sizeof(mcd_state))
|
||||
filesize = sizeof(mcd_state);
|
||||
|
||||
// align to 512K for memhandlers
|
||||
rom_alloc_size = (filesize + 0x7ffff) & ~0x7ffff;
|
||||
}
|
||||
|
||||
if (rom_alloc_size - filesize < 4)
|
||||
rom_alloc_size += 4; // padding for out-of-bound exec protection
|
||||
|
||||
// Allocate space for the rom plus padding
|
||||
rom = malloc(rom_alloc_size);
|
||||
return rom;
|
||||
}
|
||||
|
||||
int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize,int is_sms)
|
||||
{
|
||||
unsigned char *rom;
|
||||
int size, bytes_read;
|
||||
|
||||
if (f == NULL)
|
||||
return 1;
|
||||
|
||||
size = f->size;
|
||||
if (size <= 0) return 1;
|
||||
size = (size+3)&~3; // Round up to a multiple of 4
|
||||
|
||||
// Allocate space for the rom plus padding
|
||||
rom = PicoCartAlloc(size, is_sms);
|
||||
if (rom == NULL) {
|
||||
elprintf(EL_STATUS, "out of memory (wanted %i)", size);
|
||||
return 2;
|
||||
}
|
||||
|
||||
bytes_read = pm_read(rom,size,f); // Load up the rom
|
||||
if (bytes_read <= 0) {
|
||||
elprintf(EL_STATUS, "read failed");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!is_sms)
|
||||
{
|
||||
// maybe we are loading MegaCD BIOS?
|
||||
if (!(PicoAHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom+0x124, "BOOT", 4) ||
|
||||
!strncmp((char *)rom+0x128, "BOOT", 4))) {
|
||||
PicoAHW |= PAHW_MCD;
|
||||
}
|
||||
|
||||
// Check for SMD:
|
||||
if (size >= 0x4200 && (size&0x3fff) == 0x200 &&
|
||||
((rom[0x2280] == 'S' && rom[0x280] == 'E') || (rom[0x280] == 'S' && rom[0x2281] == 'E'))) {
|
||||
elprintf(EL_STATUS, "SMD format detected.");
|
||||
DecodeSmd(rom,size); size-=0x200; // Decode and byteswap SMD
|
||||
}
|
||||
else Byteswap(rom, rom, size); // Just byteswap
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size >= 0x4200 && (size&0x3fff) == 0x200) {
|
||||
elprintf(EL_STATUS, "SMD format detected.");
|
||||
// at least here it's not interleaved
|
||||
size -= 0x200;
|
||||
memmove(rom, rom + 0x200, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (prom) *prom = rom;
|
||||
if (psize) *psize = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Insert a cartridge:
|
||||
int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg)
|
||||
{
|
||||
// notaz: add a 68k "jump one op back" opcode to the end of ROM.
|
||||
// This will hang the emu, but will prevent nasty crashes.
|
||||
// note: 4 bytes are padded to every ROM
|
||||
if (rom != NULL)
|
||||
*(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped
|
||||
|
||||
Pico.rom=rom;
|
||||
Pico.romsize=romsize;
|
||||
|
||||
if (SRam.data) {
|
||||
free(SRam.data);
|
||||
SRam.data = NULL;
|
||||
}
|
||||
|
||||
PicoAHW &= PAHW_MCD|PAHW_SMS;
|
||||
|
||||
PicoCartMemSetup = NULL;
|
||||
PicoDmaHook = NULL;
|
||||
PicoResetHook = NULL;
|
||||
PicoLineHook = NULL;
|
||||
|
||||
if (!(PicoAHW & (PAHW_MCD|PAHW_SMS)))
|
||||
PicoCartDetect(carthw_cfg);
|
||||
|
||||
// setup correct memory map for loaded ROM
|
||||
switch (PicoAHW) {
|
||||
default:
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoAHW);
|
||||
case 0:
|
||||
case PAHW_SVP: PicoMemSetup(); break;
|
||||
case PAHW_MCD: PicoMemSetupCD(); break;
|
||||
case PAHW_PICO: PicoMemSetupPico(); break;
|
||||
case PAHW_SMS: PicoMemSetupMS(); break;
|
||||
}
|
||||
|
||||
if (PicoCartMemSetup != NULL)
|
||||
PicoCartMemSetup();
|
||||
|
||||
if (PicoAHW & PAHW_SMS)
|
||||
PicoPowerMS();
|
||||
else
|
||||
PicoPower();
|
||||
|
||||
PicoGameLoaded = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rom_crc32(void)
|
||||
{
|
||||
unsigned int crc;
|
||||
elprintf(EL_STATUS, "caclulating CRC32..");
|
||||
|
||||
// have to unbyteswap for calculation..
|
||||
Byteswap(Pico.rom, Pico.rom, Pico.romsize);
|
||||
crc = crc32(Pico.rom, Pico.romsize);
|
||||
Byteswap(Pico.rom, Pico.rom, Pico.romsize);
|
||||
return crc;
|
||||
}
|
||||
|
||||
static int rom_strcmp(int rom_offset, const char *s1)
|
||||
{
|
||||
int i, len = strlen(s1);
|
||||
const char *s_rom = (const char *)Pico.rom;
|
||||
if (rom_offset + len > Pico.romsize)
|
||||
return 0;
|
||||
for (i = 0; i < len; i++)
|
||||
if (s1[i] != s_rom[(i + rom_offset) ^ 1])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rom_read32(int addr)
|
||||
{
|
||||
unsigned short *m = (unsigned short *)(Pico.rom + addr);
|
||||
return (m[0] << 16) | m[1];
|
||||
}
|
||||
|
||||
static char *sskip(char *s)
|
||||
{
|
||||
while (*s && isspace_(*s))
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void rstrip(char *s)
|
||||
{
|
||||
char *p;
|
||||
for (p = s + strlen(s) - 1; p >= s; p--)
|
||||
if (isspace_(*p))
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
static int parse_3_vals(char *p, int *val0, int *val1, int *val2)
|
||||
{
|
||||
char *r;
|
||||
*val0 = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
p = sskip(r);
|
||||
if (*p++ != ',')
|
||||
goto bad;
|
||||
*val1 = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
p = sskip(r);
|
||||
if (*p++ != ',')
|
||||
goto bad;
|
||||
*val2 = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_expr(const char *expr, char **pr)
|
||||
{
|
||||
int len = strlen(expr);
|
||||
char *p = *pr;
|
||||
|
||||
if (strncmp(expr, p, len) != 0)
|
||||
return 0;
|
||||
p = sskip(p + len);
|
||||
if (*p != '=')
|
||||
return 0; // wrong or malformed
|
||||
|
||||
*pr = sskip(p + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "carthw_cfg.c"
|
||||
|
||||
static void parse_carthw(const char *carthw_cfg, int *fill_sram)
|
||||
{
|
||||
int line = 0, any_checks_passed = 0, skip_sect = 0;
|
||||
const char *s, *builtin = builtin_carthw_cfg;
|
||||
int tmp, rom_crc = 0;
|
||||
char buff[256], *p, *r;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(carthw_cfg, "r");
|
||||
if (f == NULL)
|
||||
f = fopen("pico/carthw.cfg", "r");
|
||||
if (f == NULL)
|
||||
elprintf(EL_STATUS, "couldn't open carthw.cfg!");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (f != NULL) {
|
||||
p = fgets(buff, sizeof(buff), f);
|
||||
if (p == NULL)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (*builtin == 0)
|
||||
break;
|
||||
for (s = builtin; *s != 0 && *s != '\n'; s++)
|
||||
;
|
||||
while (*s == '\n')
|
||||
s++;
|
||||
tmp = s - builtin;
|
||||
if (tmp > sizeof(buff) - 1)
|
||||
tmp = sizeof(buff) - 1;
|
||||
memcpy(buff, builtin, tmp);
|
||||
buff[tmp] = 0;
|
||||
p = buff;
|
||||
builtin = s;
|
||||
}
|
||||
|
||||
line++;
|
||||
p = sskip(p);
|
||||
if (*p == 0 || *p == '#')
|
||||
continue;
|
||||
|
||||
if (*p == '[') {
|
||||
any_checks_passed = 0;
|
||||
skip_sect = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_sect)
|
||||
continue;
|
||||
|
||||
/* look for checks */
|
||||
if (is_expr("check_str", &p))
|
||||
{
|
||||
int offs;
|
||||
offs = strtoul(p, &r, 0);
|
||||
if (offs < 0 || offs > Pico.romsize) {
|
||||
elprintf(EL_STATUS, "carthw:%d: check_str offs out of range: %d\n", line, offs);
|
||||
goto bad;
|
||||
}
|
||||
p = sskip(r);
|
||||
if (*p != ',')
|
||||
goto bad;
|
||||
p = sskip(p + 1);
|
||||
if (*p != '"')
|
||||
goto bad;
|
||||
p++;
|
||||
r = strchr(p, '"');
|
||||
if (r == NULL)
|
||||
goto bad;
|
||||
*r = 0;
|
||||
|
||||
if (rom_strcmp(offs, p) == 0)
|
||||
any_checks_passed = 1;
|
||||
else
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("check_size_gt", &p))
|
||||
{
|
||||
int size;
|
||||
size = strtoul(p, &r, 0);
|
||||
if (r == p || size < 0)
|
||||
goto bad;
|
||||
|
||||
if (Pico.romsize > size)
|
||||
any_checks_passed = 1;
|
||||
else
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("check_csum", &p))
|
||||
{
|
||||
int csum;
|
||||
csum = strtoul(p, &r, 0);
|
||||
if (r == p || (csum & 0xffff0000))
|
||||
goto bad;
|
||||
|
||||
if (csum == (rom_read32(0x18c) & 0xffff))
|
||||
any_checks_passed = 1;
|
||||
else
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("check_crc32", &p))
|
||||
{
|
||||
unsigned int crc;
|
||||
crc = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
|
||||
if (rom_crc == 0)
|
||||
rom_crc = rom_crc32();
|
||||
if (crc == rom_crc)
|
||||
any_checks_passed = 1;
|
||||
else
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* now time for actions */
|
||||
if (is_expr("hw", &p)) {
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
if (strcmp(p, "svp") == 0)
|
||||
PicoSVPStartup();
|
||||
else if (strcmp(p, "pico") == 0)
|
||||
PicoInitPico();
|
||||
else if (strcmp(p, "prot") == 0)
|
||||
carthw_sprot_startup();
|
||||
else if (strcmp(p, "ssf2_mapper") == 0)
|
||||
carthw_ssf2_startup();
|
||||
else if (strcmp(p, "x_in_1_mapper") == 0)
|
||||
carthw_Xin1_startup();
|
||||
else if (strcmp(p, "realtec_mapper") == 0)
|
||||
carthw_realtec_startup();
|
||||
else if (strcmp(p, "radica_mapper") == 0)
|
||||
carthw_radica_startup();
|
||||
else if (strcmp(p, "piersolar_mapper") == 0)
|
||||
carthw_pier_startup();
|
||||
else if (strcmp(p, "prot_lk3") == 0)
|
||||
carthw_prot_lk3_startup();
|
||||
else {
|
||||
elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p);
|
||||
skip_sect = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (is_expr("sram_range", &p)) {
|
||||
int start, end;
|
||||
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
start = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
p = sskip(r);
|
||||
if (*p != ',')
|
||||
goto bad;
|
||||
p = sskip(p + 1);
|
||||
end = strtoul(p, &r, 0);
|
||||
if (r == p)
|
||||
goto bad;
|
||||
if (((start | end) & 0xff000000) || start > end) {
|
||||
elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end);
|
||||
goto bad_nomsg;
|
||||
}
|
||||
SRam.start = start;
|
||||
SRam.end = end;
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("prop", &p)) {
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
if (strcmp(p, "no_sram") == 0)
|
||||
SRam.flags &= ~SRF_ENABLED;
|
||||
else if (strcmp(p, "no_eeprom") == 0)
|
||||
SRam.flags &= ~SRF_EEPROM;
|
||||
else if (strcmp(p, "filled_sram") == 0)
|
||||
*fill_sram = 1;
|
||||
else if (strcmp(p, "force_6btn") == 0)
|
||||
PicoQuirks |= PQUIRK_FORCE_6BTN;
|
||||
else {
|
||||
elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p);
|
||||
goto bad_nomsg;
|
||||
}
|
||||
elprintf(EL_STATUS, "game prop: %s", p);
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("eeprom_type", &p)) {
|
||||
int type;
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
type = strtoul(p, &r, 0);
|
||||
if (r == p || type < 0)
|
||||
goto bad;
|
||||
SRam.eeprom_type = type;
|
||||
SRam.flags |= SRF_EEPROM;
|
||||
continue;
|
||||
}
|
||||
else if (is_expr("eeprom_lines", &p)) {
|
||||
int scl, sda_in, sda_out;
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
if (!parse_3_vals(p, &scl, &sda_in, &sda_out))
|
||||
goto bad;
|
||||
if (scl < 0 || scl > 15 || sda_in < 0 || sda_in > 15 ||
|
||||
sda_out < 0 || sda_out > 15)
|
||||
goto bad;
|
||||
|
||||
SRam.eeprom_bit_cl = scl;
|
||||
SRam.eeprom_bit_in = sda_in;
|
||||
SRam.eeprom_bit_out= sda_out;
|
||||
continue;
|
||||
}
|
||||
else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) {
|
||||
int addr, mask, val;
|
||||
if (!any_checks_passed)
|
||||
goto no_checks;
|
||||
rstrip(p);
|
||||
|
||||
if (!parse_3_vals(p, &addr, &mask, &val))
|
||||
goto bad;
|
||||
|
||||
carthw_sprot_new_location(addr, mask, val, tmp ? 1 : 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
bad:
|
||||
elprintf(EL_STATUS, "carthw:%d: unrecognized expression: %s", line, buff);
|
||||
bad_nomsg:
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
|
||||
no_checks:
|
||||
elprintf(EL_STATUS, "carthw:%d: command without any checks before it: %s", line, buff);
|
||||
skip_sect = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
* various cart-specific things, which can't be handled by generic code
|
||||
*/
|
||||
static void PicoCartDetect(const char *carthw_cfg)
|
||||
{
|
||||
int fill_sram = 0;
|
||||
|
||||
memset(&SRam, 0, sizeof(SRam));
|
||||
if (Pico.rom[0x1B1] == 'R' && Pico.rom[0x1B0] == 'A')
|
||||
{
|
||||
SRam.start = rom_read32(0x1B4) & ~0xff000001; // align
|
||||
SRam.end = (rom_read32(0x1B8) & ~0xff000000) | 1;
|
||||
if (Pico.rom[0x1B2] & 0x40)
|
||||
// EEPROM
|
||||
SRam.flags |= SRF_EEPROM;
|
||||
SRam.flags |= SRF_ENABLED;
|
||||
}
|
||||
if (SRam.end == 0 || SRam.start > SRam.end)
|
||||
{
|
||||
// some games may have bad headers, like S&K and Sonic3
|
||||
// note: majority games use 0x200000 as starting address, but there are some which
|
||||
// use something else (0x300000 by HardBall '95). Luckily they have good headers.
|
||||
SRam.start = 0x200000;
|
||||
SRam.end = 0x203FFF;
|
||||
SRam.flags |= SRF_ENABLED;
|
||||
}
|
||||
|
||||
// set EEPROM defaults, in case it gets detected
|
||||
SRam.eeprom_type = 0; // 7bit (24C01)
|
||||
SRam.eeprom_bit_cl = 1;
|
||||
SRam.eeprom_bit_in = 0;
|
||||
SRam.eeprom_bit_out= 0;
|
||||
|
||||
if (carthw_cfg != NULL)
|
||||
parse_carthw(carthw_cfg, &fill_sram);
|
||||
|
||||
if (SRam.flags & SRF_ENABLED)
|
||||
{
|
||||
if (SRam.flags & SRF_EEPROM)
|
||||
SRam.size = 0x2000;
|
||||
else
|
||||
SRam.size = SRam.end - SRam.start + 1;
|
||||
|
||||
SRam.data = calloc(SRam.size, 1);
|
||||
if (SRam.data == NULL)
|
||||
SRam.flags &= ~SRF_ENABLED;
|
||||
|
||||
if (SRam.eeprom_type == 1) // 1 == 0 in PD EEPROM code
|
||||
SRam.eeprom_type = 0;
|
||||
}
|
||||
|
||||
if ((SRam.flags & SRF_ENABLED) && fill_sram)
|
||||
{
|
||||
elprintf(EL_STATUS, "SRAM fill");
|
||||
memset(SRam.data, 0xff, SRam.size);
|
||||
}
|
||||
|
||||
// Unusual region 'code'
|
||||
if (rom_strcmp(0x1f0, "EUROPE") == 0 || rom_strcmp(0x1f0, "Europe") == 0)
|
||||
*(int *) (Pico.rom + 0x1f0) = 0x20204520;
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
# hardware (hw = ...):
|
||||
# svp - Sega Virtua Processor
|
||||
# pico - Sega Pico (not really cart hw, but convenient to support here)
|
||||
# prot - siple copy protection devices in unlicensed cartridges (see prot. below)
|
||||
# prot_lk3 - Lion King 3 / Super King Kong 99 protection.
|
||||
#
|
||||
# cartridge properties (prop = ...):
|
||||
# no_sram - don't emulate sram/EEPROM even if ROM headers tell it's there
|
||||
# no_eeprom - save storage is not EEPROM, even if ROM headers tell it is
|
||||
# filled_sram - save storage needs to be initialized with FFh instead of 00h
|
||||
# force_6btn - game only supports 6 button pad (32X X-men proto)
|
||||
#
|
||||
# mappers (hw = ...):
|
||||
# ssf2_mapper - used in Super Street Fighter2
|
||||
# x_in_1_mapper - used in many *-in-1 pirate carts
|
||||
# realtec_mapper
|
||||
# radica_mapper - similar to x_in_1_mapper
|
||||
# piersolar_mapper - used in Pier Solar
|
||||
#
|
||||
# save storage memory range (inclusive, overrides ROM header):
|
||||
# sram_range = <start, end>
|
||||
#
|
||||
# EEPROM:
|
||||
# eeprom_type = <1|2|3> - enable EEPROM, use type X (7bit, 2 addr words, 3 addr words).
|
||||
# See EkeEke's gen_eeprom.pdf "mode" descriptions for 24Cxx EEPROMs.
|
||||
# eeprom_lines = <SCL,SDA_IN,SDA_OUT>
|
||||
# - select data bus pins that are connected to EEPROM
|
||||
# SCL, SDA_IN and SDA_OUT pins (0-15 for D0-D15).
|
||||
# hw = prot:
|
||||
# prot_ro_value16 = <addr, mask, val> - return constant <val> on reads at location
|
||||
# (<addr> & <mask>), ignore writes.
|
||||
# prot_rw_value16 = <addr, mask, val> - same as above, but writeable
|
||||
|
||||
[Virtua Racing - SVP]
|
||||
check_str = 0x150, "Virtua Racing"
|
||||
check_str = 0x810, "OHMP"
|
||||
hw = svp
|
||||
|
||||
[Virtua Racing - SVP]
|
||||
check_str = 0x150, "VIRTUA RACING"
|
||||
check_str = 0x810, "OHMP"
|
||||
hw = svp
|
||||
|
||||
[Pico]
|
||||
check_str = 0x100, "SEGA PICO"
|
||||
hw = pico
|
||||
|
||||
[Pico]
|
||||
check_str = 0x100, "IMA IKUNOUJYUKU"
|
||||
hw = pico
|
||||
|
||||
# sram emulation triggers some protection for this one
|
||||
[Puggsy]
|
||||
check_str = 0x120, "PUGGSY"
|
||||
prop = no_sram
|
||||
|
||||
# game has problems if it's save RAM is not initialized with FFh:
|
||||
[Dino Dini's Soccer]
|
||||
check_str = 0x150, "DINO DINI'S SOCCER"
|
||||
prop = filled_sram
|
||||
|
||||
[Micro Machines 2 - Turbo Tournament]
|
||||
check_str = 0x150, "MICRO MACHINES II"
|
||||
prop = filled_sram
|
||||
|
||||
# X-Men proto
|
||||
[X-Men (prototype)]
|
||||
check_str = 0x150, "32X SAMPLE PROGRAM"
|
||||
check_str = 0x32b74c, "Bishop Level"
|
||||
prop = force_6btn
|
||||
|
||||
# The SSF2 mapper
|
||||
[Super Street Fighter II - The New Challengers (U)]
|
||||
check_str = 0x150, "SUPER STREET FIGHTER2 The New Challengers"
|
||||
hw = ssf2_mapper
|
||||
prop = no_sram
|
||||
|
||||
# The Pier Solar mapper, custom eeprom location
|
||||
[Pier Solar and the Great Architects]
|
||||
check_str = 0x150, "PIER"
|
||||
check_str = 0x610, "Respect"
|
||||
hw = piersolar_mapper
|
||||
|
||||
# detect *_in_1 based on first game and if it's larger than it should be,
|
||||
# as some dumps look like to be incomplete.
|
||||
# This will also pick overdumps, but those should still work with the mapper applied.
|
||||
[12-in-1 (Unl)]
|
||||
check_str = 0x120, "FLICKY"
|
||||
check_size_gt = 0x020000
|
||||
hw = x_in_1_mapper
|
||||
|
||||
[4-in-1]
|
||||
check_str = 0x150, "ROBOCOP 3"
|
||||
check_size_gt = 0x080000
|
||||
hw = x_in_1_mapper
|
||||
|
||||
[4-in-1 a1]
|
||||
check_str = 0x150, "ALIEN 3"
|
||||
check_size_gt = 0x080000
|
||||
hw = x_in_1_mapper
|
||||
|
||||
[Super 15-in-1]
|
||||
check_str = 0x150, " SHOVE IT!"
|
||||
check_size_gt = 0x020000
|
||||
hw = x_in_1_mapper
|
||||
|
||||
[Super 19-in-1]
|
||||
check_str = 0x150, "MS PACMAN"
|
||||
check_size_gt = 0x020000
|
||||
hw = x_in_1_mapper
|
||||
|
||||
# radica
|
||||
[radica_v1]
|
||||
check_str = 0x150, "KID CHAMELEON"
|
||||
check_size_gt = 0x100000
|
||||
hw = radica_mapper
|
||||
|
||||
# realtec
|
||||
[Earth Defend, The (Unl)]
|
||||
check_str = 0x94, "THE EARTH DEFEND"
|
||||
hw = realtec_mapper
|
||||
|
||||
[Funny World & Balloon Boy (Unl)]
|
||||
check_str = 0xfe, "WISEGAME 11-03-1993"
|
||||
hw = realtec_mapper
|
||||
|
||||
[Whac-A-Critter (Unl)]
|
||||
check_str = 0x95, "MALLET LEGEND"
|
||||
hw = realtec_mapper
|
||||
|
||||
# EEPROM games - thanks to EkeEke for most of this info
|
||||
[College Slam]
|
||||
check_str = 0x150, "COLLEGE SLAM"
|
||||
eeprom_type = 3
|
||||
eeprom_lines = 8,0,0
|
||||
|
||||
[Frank Thomas Big Hurt Baseball]
|
||||
check_str = 0x150, "FRANK THOMAS BIGHURT BASEBAL"
|
||||
eeprom_type = 3
|
||||
eeprom_lines = 8,0,0
|
||||
|
||||
[MICRO MACHINES II]
|
||||
check_str = 0x150, "MICRO MACHINES II"
|
||||
sram_range = 0x300000,0x380001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 9,8,7
|
||||
|
||||
[Micro Machines - Turbo Tournament '96]
|
||||
check_str = 0x150, " "
|
||||
check_csum = 0x165e
|
||||
sram_range = 0x300000,0x380001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 9,8,7
|
||||
|
||||
[Micro Machines - Turbo Tournament '96]
|
||||
check_str = 0x150, " "
|
||||
check_csum = 0x2c41
|
||||
sram_range = 0x300000,0x380001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 9,8,7
|
||||
|
||||
[Micro Machines Military]
|
||||
check_str = 0x150, " "
|
||||
check_csum = 0x168b
|
||||
sram_range = 0x300000,0x380001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 9,8,7
|
||||
|
||||
[Micro Machines Military]
|
||||
check_str = 0x150, " "
|
||||
check_csum = 0xcee0
|
||||
sram_range = 0x300000,0x380001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 9,8,7
|
||||
|
||||
[NBA Jam]
|
||||
check_str = 0x150, "NBA JAM "
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 1,0,1
|
||||
|
||||
[NBA Jam Tournament Edition]
|
||||
check_str = 0x150, "NBA JAM TOURNAMENT EDITION"
|
||||
sram_range = 0x200000,0x200001
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 8,0,0
|
||||
|
||||
[NFL Quarterback Club]
|
||||
check_str = 0x150, "NFL QUARTERBACK CLUB"
|
||||
eeprom_type = 2
|
||||
eeprom_lines = 8,0,0
|
||||
|
||||
[NHLPA Hockey '93]
|
||||
check_str = 0x150, "NHLPA Hockey '93"
|
||||
sram_range = 0x200000,0x200001
|
||||
eeprom_type = 1
|
||||
eeprom_lines = 6,7,7
|
||||
|
||||
[NHLPA Hockey '93]
|
||||
check_str = 0x150, "NHLPA HOCKEY '93"
|
||||
sram_range = 0x200000,0x200001
|
||||
eeprom_type = 1
|
||||
eeprom_lines = 6,7,7
|
||||
|
||||
[Rings of Power]
|
||||
check_str = 0x150, "RINGS OF POWER"
|
||||
sram_range = 0x200000,0x200001
|
||||
eeprom_type = 1
|
||||
eeprom_lines = 6,7,7
|
||||
|
||||
# Unlicensed games with simple protections
|
||||
# some of these come from Haze, some from myself (notaz).
|
||||
[Bug's Life, A (Unl)]
|
||||
check_str = 0x104, " "
|
||||
check_crc32 = 0x10458e09
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13000,0xffff00,0x28
|
||||
|
||||
[Elf Wor (Unl)]
|
||||
check_str = 0x172, "GAME : ELF WOR"
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x400000,-2,0x5500
|
||||
prot_ro_value16 = 0x400002,-2,0xc900 # check is done if the above one fails
|
||||
prot_ro_value16 = 0x400004,-2,0x0f00
|
||||
prot_ro_value16 = 0x400006,-2,0x1800 # similar to above
|
||||
|
||||
[King of Fighters '98, The (Unl)]
|
||||
check_str = 0x104, " "
|
||||
check_crc32 = 0xcbc38eea
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x480000,0xff0000,0xaa00
|
||||
prot_ro_value16 = 0x4a0000,0xff0000,0x0a00
|
||||
prot_ro_value16 = 0x4c0000,0xff0000,0xf000
|
||||
prot_ro_value16 = 0x400000,0xc00000,0x0000 # default for 400000-7f0000
|
||||
|
||||
[Lion King 3 (Unl)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0xc004219d
|
||||
hw = prot_lk3
|
||||
|
||||
[Lion King II, The (Unl)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0xaff46765
|
||||
hw = prot
|
||||
prot_rw_value16 = 0x400000,0xc00004,0
|
||||
prot_rw_value16 = 0x400004,0xc00004,0
|
||||
|
||||
[Mahjong Lover (Unl)]
|
||||
check_str = 0x118, "CREATON. "
|
||||
check_crc32 = 0xddd02ba4
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x400000,-2,0x9000
|
||||
prot_ro_value16 = 0x401000,-2,0xd300
|
||||
|
||||
[Pocket Monsters (Unl)]
|
||||
check_str = 0x104, " "
|
||||
check_crc32 = 0xf68f6367
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13002,-2,0x01
|
||||
prot_ro_value16 = 0xa1303e,-2,0x1f
|
||||
|
||||
[Pocket Monsters (Unl) [a1]]
|
||||
check_str = 0x104, " "
|
||||
check_crc32 = 0xfb176667
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13000,-2,0x14
|
||||
prot_ro_value16 = 0xa13002,-2,0x01
|
||||
prot_ro_value16 = 0xa1303e,-2,0x1f
|
||||
|
||||
[Rockman X3 (Unl)]
|
||||
check_csum = 0
|
||||
check_crc32 = 0x3ee639f0
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13000,-2,0x0c
|
||||
|
||||
[Smart Mouse (Unl)]
|
||||
check_csum = 0
|
||||
check_crc32 = 0xdecdf740
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x400000,-2,0x5500
|
||||
prot_ro_value16 = 0x400002,-2,0x0f00
|
||||
prot_ro_value16 = 0x400004,-2,0xaa00
|
||||
prot_ro_value16 = 0x400006,-2,0xf000
|
||||
|
||||
[Soul Blade (Unl)]
|
||||
check_str = 0x104, " "
|
||||
check_crc32 = 0xf26f88d1
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x400002,-2,0x9800
|
||||
prot_ro_value16 = 0x400004,-2,0xaa00 # or 0xc900
|
||||
prot_ro_value16 = 0x400006,-2,0xf000
|
||||
|
||||
[Super Bubble Bobble (Unl)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0x4820a161
|
||||
hw = prot
|
||||
prot_ro_value16 = 0x400000,-2,0x5500
|
||||
prot_ro_value16 = 0x400002,-2,0x0f00
|
||||
|
||||
[Super King Kong 99 (Unl)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0x413dfee2
|
||||
hw = prot_lk3
|
||||
|
||||
[Super Mario Bros. (Unl)]
|
||||
check_str = 0x140, "SUPER MARIO BROS "
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13000,-2,0x0c
|
||||
|
||||
[Super Mario 2 1998 (Unl)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0xf7e1b3e1
|
||||
hw = prot
|
||||
prot_ro_value16 = 0xa13000,-2,0x0a
|
||||
|
||||
[Squirrel King (R)]
|
||||
check_str = 0x104, " are Registered Trademarks"
|
||||
check_crc32 = 0xb8261ff5
|
||||
hw = prot
|
||||
prot_rw_value16 = 0x400000,0xc00000,0
|
||||
|
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* Support for a few cart mappers and some protection.
|
||||
* (C) notaz, 2008-2011
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
#include "../memory.h"
|
||||
|
||||
|
||||
/* The SSFII mapper */
|
||||
static unsigned char ssf2_banks[8];
|
||||
|
||||
static void carthw_ssf2_write8(u32 a, u32 d)
|
||||
{
|
||||
u32 target, base;
|
||||
|
||||
if ((a & 0xfffff0) != 0xa130f0) {
|
||||
PicoWrite8_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
a &= 0x0e;
|
||||
if (a == 0)
|
||||
return;
|
||||
|
||||
ssf2_banks[a >> 1] = d;
|
||||
base = d << 19;
|
||||
target = a << 18;
|
||||
if (base + 0x80000 > Pico.romsize) {
|
||||
elprintf(EL_ANOMALY|EL_STATUS, "ssf2: missing bank @ %06x", base);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu68k_map_set(m68k_read8_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
|
||||
cpu68k_map_set(m68k_read16_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
|
||||
}
|
||||
|
||||
static void carthw_ssf2_mem_setup(void)
|
||||
{
|
||||
cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, carthw_ssf2_write8, 1);
|
||||
}
|
||||
|
||||
void carthw_ssf2_startup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
elprintf(EL_STATUS, "SSF2 mapper startup");
|
||||
|
||||
// default map
|
||||
for (i = 0; i < 8; i++)
|
||||
ssf2_banks[i] = i;
|
||||
|
||||
PicoCartMemSetup = carthw_ssf2_mem_setup;
|
||||
}
|
||||
|
||||
|
||||
/* Common *-in-1 pirate mapper.
|
||||
* Switches banks based on addr lines when /TIME is set.
|
||||
* TODO: verify
|
||||
*/
|
||||
static unsigned int carthw_Xin1_baddr = 0;
|
||||
|
||||
static void carthw_Xin1_do(u32 a, int mask, int shift)
|
||||
{
|
||||
int len;
|
||||
|
||||
carthw_Xin1_baddr = a;
|
||||
a &= mask;
|
||||
a <<= shift;
|
||||
len = Pico.romsize - a;
|
||||
if (len <= 0) {
|
||||
elprintf(EL_ANOMALY|EL_STATUS, "X-in-1: missing bank @ %06x", a);
|
||||
return;
|
||||
}
|
||||
|
||||
len = (len + M68K_BANK_MASK) & ~M68K_BANK_MASK;
|
||||
cpu68k_map_set(m68k_read8_map, 0x000000, len - 1, Pico.rom + a, 0);
|
||||
cpu68k_map_set(m68k_read16_map, 0x000000, len - 1, Pico.rom + a, 0);
|
||||
}
|
||||
|
||||
// TODO: test a0, reads, w16
|
||||
static void carthw_Xin1_write8(u32 a, u32 d)
|
||||
{
|
||||
if ((a & 0xffff00) != 0xa13000) {
|
||||
PicoWrite8_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
carthw_Xin1_do(a, 0x3f, 16);
|
||||
}
|
||||
|
||||
static void carthw_Xin1_mem_setup(void)
|
||||
{
|
||||
cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, carthw_Xin1_write8, 1);
|
||||
}
|
||||
|
||||
static void carthw_Xin1_reset(void)
|
||||
{
|
||||
carthw_Xin1_write8(0xa13000, 0);
|
||||
}
|
||||
|
||||
void carthw_Xin1_startup(void)
|
||||
{
|
||||
elprintf(EL_STATUS, "X-in-1 mapper startup");
|
||||
|
||||
PicoCartMemSetup = carthw_Xin1_mem_setup;
|
||||
PicoResetHook = carthw_Xin1_reset;
|
||||
}
|
||||
|
||||
|
||||
/* Realtec, based on TascoDLX doc
|
||||
* http://www.sharemation.com/TascoDLX/REALTEC%20Cart%20Mapper%20-%20description%20v1.txt
|
||||
*/
|
||||
static int realtec_bank = 0x80000000, realtec_size = 0x80000000;
|
||||
|
||||
static void carthw_realtec_write8(u32 a, u32 d)
|
||||
{
|
||||
int i, bank_old = realtec_bank, size_old = realtec_size;
|
||||
|
||||
if (a == 0x400000)
|
||||
{
|
||||
realtec_bank &= 0x0e0000;
|
||||
realtec_bank |= 0x300000 & (d << 19);
|
||||
if (realtec_bank != bank_old)
|
||||
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
|
||||
}
|
||||
else if (a == 0x402000)
|
||||
{
|
||||
realtec_size = (d << 17) & 0x3e0000;
|
||||
if (realtec_size != size_old)
|
||||
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
|
||||
}
|
||||
else if (a == 0x404000)
|
||||
{
|
||||
realtec_bank &= 0x300000;
|
||||
realtec_bank |= 0x0e0000 & (d << 17);
|
||||
if (realtec_bank != bank_old)
|
||||
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
|
||||
}
|
||||
else
|
||||
elprintf(EL_ANOMALY, "realtec: unexpected write [%06x] %02x @ %06x", a, d, SekPc);
|
||||
|
||||
if (realtec_bank >= 0 && realtec_size >= 0 &&
|
||||
(realtec_bank != bank_old || realtec_size != size_old))
|
||||
{
|
||||
elprintf(EL_ANOMALY, "realtec: new bank %06x, size %06x", realtec_bank, realtec_size, SekPc);
|
||||
if (realtec_size > Pico.romsize - realtec_bank)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "realtec: bank too large / out of range?");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < 0x400000; i += realtec_size) {
|
||||
cpu68k_map_set(m68k_read8_map, i, realtec_size - 1, Pico.rom + realtec_bank, 0);
|
||||
cpu68k_map_set(m68k_read16_map, i, realtec_size - 1, Pico.rom + realtec_bank, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void carthw_realtec_reset(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* map boot code */
|
||||
for (i = 0; i < 0x400000; i += M68K_BANK_SIZE) {
|
||||
cpu68k_map_set(m68k_read8_map, i, i + M68K_BANK_SIZE - 1, Pico.rom + Pico.romsize, 0);
|
||||
cpu68k_map_set(m68k_read16_map, i, i + M68K_BANK_SIZE - 1, Pico.rom + Pico.romsize, 0);
|
||||
}
|
||||
cpu68k_map_set(m68k_write8_map, 0x400000, 0x400000 + M68K_BANK_SIZE - 1, carthw_realtec_write8, 1);
|
||||
realtec_bank = realtec_size = 0x80000000;
|
||||
}
|
||||
|
||||
void carthw_realtec_startup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
elprintf(EL_STATUS, "Realtec mapper startup");
|
||||
|
||||
// allocate additional bank for boot code
|
||||
// (we know those ROMs have aligned size)
|
||||
i = PicoCartResize(Pico.romsize + M68K_BANK_SIZE);
|
||||
if (i != 0) {
|
||||
elprintf(EL_STATUS, "OOM");
|
||||
return;
|
||||
}
|
||||
|
||||
// create bank for boot code
|
||||
for (i = 0; i < M68K_BANK_SIZE; i += 0x2000)
|
||||
memcpy(Pico.rom + Pico.romsize + i, Pico.rom + Pico.romsize - 0x2000, 0x2000);
|
||||
|
||||
PicoResetHook = carthw_realtec_reset;
|
||||
}
|
||||
|
||||
/* Radica mapper, based on DevSter's info
|
||||
* http://devster.monkeeh.com/sega/radica/
|
||||
* XXX: mostly the same as X-in-1, merge?
|
||||
*/
|
||||
static u32 carthw_radica_read16(u32 a)
|
||||
{
|
||||
if ((a & 0xffff00) != 0xa13000)
|
||||
return PicoRead16_io(a);
|
||||
|
||||
carthw_Xin1_do(a, 0x7e, 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void carthw_radica_mem_setup(void)
|
||||
{
|
||||
cpu68k_map_set(m68k_read16_map, 0xa10000, 0xa1ffff, carthw_radica_read16, 1);
|
||||
}
|
||||
|
||||
static void carthw_radica_reset(void)
|
||||
{
|
||||
carthw_radica_read16(0xa13000);
|
||||
}
|
||||
|
||||
void carthw_radica_startup(void)
|
||||
{
|
||||
elprintf(EL_STATUS, "Radica mapper startup");
|
||||
|
||||
PicoCartMemSetup = carthw_radica_mem_setup;
|
||||
PicoResetHook = carthw_radica_reset;
|
||||
}
|
||||
|
||||
|
||||
/* Pier Solar. Based on my own research */
|
||||
static unsigned char pier_regs[8];
|
||||
static unsigned char pier_dump_prot;
|
||||
|
||||
static void carthw_pier_write8(u32 a, u32 d)
|
||||
{
|
||||
u32 a8, target, base;
|
||||
|
||||
if ((a & 0xffff00) != 0xa13000) {
|
||||
PicoWrite8_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
a8 = a & 0x0f;
|
||||
pier_regs[a8 / 2] = d;
|
||||
|
||||
elprintf(EL_UIO, "pier w8 [%06x] %02x @%06x", a, d & 0xffff, SekPc);
|
||||
switch (a8) {
|
||||
case 0x01:
|
||||
break;
|
||||
case 0x03:
|
||||
if (!(pier_regs[0] & 2))
|
||||
goto unmapped;
|
||||
target = 0x280000;
|
||||
base = d << 19;
|
||||
goto do_map;
|
||||
case 0x05:
|
||||
if (!(pier_regs[0] & 2))
|
||||
goto unmapped;
|
||||
target = 0x300000;
|
||||
base = d << 19;
|
||||
goto do_map;
|
||||
case 0x07:
|
||||
if (!(pier_regs[0] & 2))
|
||||
goto unmapped;
|
||||
target = 0x380000;
|
||||
base = d << 19;
|
||||
goto do_map;
|
||||
case 0x09:
|
||||
// TODO
|
||||
break;
|
||||
case 0x0b:
|
||||
// eeprom read
|
||||
default:
|
||||
unmapped:
|
||||
//elprintf(EL_UIO, "pier w8 [%06x] %02x @%06x", a, d & 0xffff, SekPc);
|
||||
elprintf(EL_STATUS, "-- unmapped w8 [%06x] %02x @%06x", a, d & 0xffff, SekPc);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
do_map:
|
||||
if (base + 0x80000 > Pico.romsize) {
|
||||
elprintf(EL_ANOMALY|EL_STATUS, "pier: missing bank @ %06x", base);
|
||||
return;
|
||||
}
|
||||
cpu68k_map_set(m68k_read8_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
|
||||
cpu68k_map_set(m68k_read16_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
|
||||
}
|
||||
|
||||
static void carthw_pier_write16(u32 a, u32 d)
|
||||
{
|
||||
if ((a & 0xffff00) != 0xa13000) {
|
||||
PicoWrite16_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
elprintf(EL_UIO, "pier w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
|
||||
carthw_pier_write8(a + 1, d);
|
||||
}
|
||||
|
||||
static u32 carthw_pier_read8(u32 a)
|
||||
{
|
||||
if ((a & 0xffff00) != 0xa13000)
|
||||
return PicoRead8_io(a);
|
||||
|
||||
if (a == 0xa1300b)
|
||||
return 0; // TODO
|
||||
|
||||
elprintf(EL_UIO, "pier r8 [%06x] @%06x", a, SekPc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void carthw_pier_statef(void);
|
||||
|
||||
static u32 carthw_pier_prot_read8(u32 a)
|
||||
{
|
||||
/* it takes more than just these reads here to disable ROM protection,
|
||||
* but for game emulation purposes this is enough. */
|
||||
if (pier_dump_prot > 0)
|
||||
pier_dump_prot--;
|
||||
if (pier_dump_prot == 0) {
|
||||
carthw_pier_statef();
|
||||
elprintf(EL_STATUS, "prot off on r8 @%06x", SekPc);
|
||||
}
|
||||
elprintf(EL_UIO, "pier r8 [%06x] @%06x", a, SekPc);
|
||||
|
||||
return Pico.rom[(a & 0x7fff) ^ 1];
|
||||
}
|
||||
|
||||
static void carthw_pier_mem_setup(void)
|
||||
{
|
||||
cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, carthw_pier_write8, 1);
|
||||
cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, carthw_pier_write16, 1);
|
||||
cpu68k_map_set(m68k_read8_map, 0xa10000, 0xa1ffff, carthw_pier_read8, 1);
|
||||
}
|
||||
|
||||
static void carthw_pier_prot_mem_setup(int prot_enable)
|
||||
{
|
||||
if (prot_enable) {
|
||||
/* the dump protection.. */
|
||||
int a;
|
||||
for (a = 0x000000; a < 0x400000; a += M68K_BANK_SIZE) {
|
||||
cpu68k_map_set(m68k_read8_map, a, a + 0xffff, Pico.rom + Pico.romsize, 0);
|
||||
cpu68k_map_set(m68k_read16_map, a, a + 0xffff, Pico.rom + Pico.romsize, 0);
|
||||
}
|
||||
cpu68k_map_set(m68k_read8_map, M68K_BANK_SIZE, M68K_BANK_SIZE * 2 - 1,
|
||||
carthw_pier_prot_read8, 1);
|
||||
}
|
||||
else {
|
||||
cpu68k_map_set(m68k_read8_map, 0, 0x27ffff, Pico.rom, 0);
|
||||
cpu68k_map_set(m68k_read16_map, 0, 0x27ffff, Pico.rom, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void carthw_pier_statef(void)
|
||||
{
|
||||
carthw_pier_prot_mem_setup(pier_dump_prot);
|
||||
|
||||
if (!pier_dump_prot) {
|
||||
/* setup all banks */
|
||||
u32 r0 = pier_regs[0];
|
||||
carthw_pier_write8(0xa13001, 3);
|
||||
carthw_pier_write8(0xa13003, pier_regs[1]);
|
||||
carthw_pier_write8(0xa13005, pier_regs[2]);
|
||||
carthw_pier_write8(0xa13007, pier_regs[3]);
|
||||
carthw_pier_write8(0xa13001, r0);
|
||||
}
|
||||
}
|
||||
|
||||
static void carthw_pier_reset(void)
|
||||
{
|
||||
pier_regs[0] = 1;
|
||||
pier_regs[1] = pier_regs[2] = pier_regs[3] = 0;
|
||||
pier_dump_prot = 3;
|
||||
carthw_pier_statef();
|
||||
}
|
||||
|
||||
void carthw_pier_startup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
elprintf(EL_STATUS, "Pier Solar mapper startup");
|
||||
|
||||
// mostly same as for realtec..
|
||||
i = PicoCartResize(Pico.romsize + M68K_BANK_SIZE);
|
||||
if (i != 0) {
|
||||
elprintf(EL_STATUS, "OOM");
|
||||
return;
|
||||
}
|
||||
|
||||
// create dump protection bank
|
||||
for (i = 0; i < M68K_BANK_SIZE; i += 0x8000)
|
||||
memcpy(Pico.rom + Pico.romsize + i, Pico.rom, 0x8000);
|
||||
|
||||
PicoCartMemSetup = carthw_pier_mem_setup;
|
||||
PicoResetHook = carthw_pier_reset;
|
||||
}
|
||||
|
||||
/* Simple unlicensed ROM protection emulation */
|
||||
static struct {
|
||||
u32 addr;
|
||||
u32 mask;
|
||||
u16 val;
|
||||
u16 readonly;
|
||||
} *sprot_items;
|
||||
static int sprot_item_alloc;
|
||||
static int sprot_item_count;
|
||||
|
||||
static u16 *carthw_sprot_get_val(u32 a, int rw_only)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sprot_item_count; i++)
|
||||
if ((a & sprot_items[i].mask) == sprot_items[i].addr)
|
||||
if (!rw_only || !sprot_items[i].readonly)
|
||||
return &sprot_items[i].val;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 PicoRead8_sprot(u32 a)
|
||||
{
|
||||
u16 *val;
|
||||
u32 d;
|
||||
|
||||
if (0xa10000 <= a && a < 0xa12000)
|
||||
return PicoRead8_io(a);
|
||||
|
||||
val = carthw_sprot_get_val(a, 0);
|
||||
if (val != NULL) {
|
||||
d = *val;
|
||||
if (!(a & 1))
|
||||
d >>= 8;
|
||||
elprintf(EL_UIO, "prot r8 [%06x] %02x @%06x", a, d, SekPc);
|
||||
return d;
|
||||
}
|
||||
else {
|
||||
elprintf(EL_UIO, "prot r8 [%06x] MISS @%06x", a, SekPc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 PicoRead16_sprot(u32 a)
|
||||
{
|
||||
u16 *val;
|
||||
|
||||
if (0xa10000 <= a && a < 0xa12000)
|
||||
return PicoRead16_io(a);
|
||||
|
||||
val = carthw_sprot_get_val(a, 0);
|
||||
if (val != NULL) {
|
||||
elprintf(EL_UIO, "prot r16 [%06x] %04x @%06x", a, *val, SekPc);
|
||||
return *val;
|
||||
}
|
||||
else {
|
||||
elprintf(EL_UIO, "prot r16 [%06x] MISS @%06x", a, SekPc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void PicoWrite8_sprot(u32 a, u32 d)
|
||||
{
|
||||
u16 *val;
|
||||
|
||||
if (0xa10000 <= a && a < 0xa12000) {
|
||||
PicoWrite8_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
val = carthw_sprot_get_val(a, 1);
|
||||
if (val != NULL) {
|
||||
if (a & 1)
|
||||
*val = (*val & 0xff00) | (d | 0xff);
|
||||
else
|
||||
*val = (*val & 0x00ff) | (d << 8);
|
||||
elprintf(EL_UIO, "prot w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
|
||||
}
|
||||
else
|
||||
elprintf(EL_UIO, "prot w8 [%06x] %02x MISS @%06x", a, d & 0xff, SekPc);
|
||||
}
|
||||
|
||||
static void PicoWrite16_sprot(u32 a, u32 d)
|
||||
{
|
||||
u16 *val;
|
||||
|
||||
if (0xa10000 <= a && a < 0xa12000) {
|
||||
PicoWrite16_io(a, d);
|
||||
return;
|
||||
}
|
||||
|
||||
val = carthw_sprot_get_val(a, 1);
|
||||
if (val != NULL) {
|
||||
*val = d;
|
||||
elprintf(EL_UIO, "prot w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
|
||||
}
|
||||
else
|
||||
elprintf(EL_UIO, "prot w16 [%06x] %04x MISS @%06x", a, d & 0xffff, SekPc);
|
||||
}
|
||||
|
||||
void carthw_sprot_new_location(unsigned int a, unsigned int mask, unsigned short val, int is_ro)
|
||||
{
|
||||
if (sprot_items == NULL) {
|
||||
sprot_items = calloc(8, sizeof(sprot_items[0]));
|
||||
sprot_item_alloc = 8;
|
||||
sprot_item_count = 0;
|
||||
}
|
||||
|
||||
if (sprot_item_count == sprot_item_alloc) {
|
||||
void *tmp;
|
||||
sprot_item_alloc *= 2;
|
||||
tmp = realloc(sprot_items, sprot_item_alloc);
|
||||
if (tmp == NULL) {
|
||||
elprintf(EL_STATUS, "OOM");
|
||||
return;
|
||||
}
|
||||
sprot_items = tmp;
|
||||
}
|
||||
|
||||
sprot_items[sprot_item_count].addr = a;
|
||||
sprot_items[sprot_item_count].mask = mask;
|
||||
sprot_items[sprot_item_count].val = val;
|
||||
sprot_items[sprot_item_count].readonly = is_ro;
|
||||
sprot_item_count++;
|
||||
}
|
||||
|
||||
static void carthw_sprot_mem_setup(void)
|
||||
{
|
||||
int start;
|
||||
|
||||
// map ROM - 0x7fffff, /TIME areas (which are tipically used)
|
||||
start = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK;
|
||||
cpu68k_map_set(m68k_read8_map, start, 0x7fffff, PicoRead8_sprot, 1);
|
||||
cpu68k_map_set(m68k_read16_map, start, 0x7fffff, PicoRead16_sprot, 1);
|
||||
cpu68k_map_set(m68k_write8_map, start, 0x7fffff, PicoWrite8_sprot, 1);
|
||||
cpu68k_map_set(m68k_write16_map, start, 0x7fffff, PicoWrite16_sprot, 1);
|
||||
|
||||
cpu68k_map_set(m68k_read8_map, 0xa10000, 0xa1ffff, PicoRead8_sprot, 1);
|
||||
cpu68k_map_set(m68k_read16_map, 0xa10000, 0xa1ffff, PicoRead16_sprot, 1);
|
||||
cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, PicoWrite8_sprot, 1);
|
||||
cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, PicoWrite16_sprot, 1);
|
||||
}
|
||||
|
||||
void carthw_sprot_startup(void)
|
||||
{
|
||||
elprintf(EL_STATUS, "Prot emu startup");
|
||||
|
||||
PicoCartMemSetup = carthw_sprot_mem_setup;
|
||||
}
|
||||
|
||||
/* Protection emulation for Lion King 3. Credits go to Haze */
|
||||
static u8 prot_lk3_cmd, prot_lk3_data;
|
||||
|
||||
static u32 PicoRead8_plk3(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
switch (prot_lk3_cmd) {
|
||||
case 1: d = prot_lk3_data >> 1; break;
|
||||
case 2: // nibble rotate
|
||||
d = ((prot_lk3_data >> 4) | (prot_lk3_data << 4)) & 0xff;
|
||||
break;
|
||||
case 3: // bit rotate
|
||||
d = prot_lk3_data;
|
||||
d = (d >> 4) | (d << 4);
|
||||
d = ((d & 0xcc) >> 2) | ((d & 0x33) << 2);
|
||||
d = ((d & 0xaa) >> 1) | ((d & 0x55) << 1);
|
||||
break;
|
||||
/* Top Fighter 2000 MK VIII (Unl)
|
||||
case 0x98: d = 0x50; break; // prot_lk3_data == a8 here
|
||||
case 0x67: d = 0xde; break; // prot_lk3_data == 7b here (rot!)
|
||||
case 0xb5: d = 0x9f; break; // prot_lk3_data == 4a
|
||||
*/
|
||||
default:
|
||||
elprintf(EL_UIO, "unhandled prot cmd %02x @%06x", prot_lk3_cmd, SekPc);
|
||||
break;
|
||||
}
|
||||
|
||||
elprintf(EL_UIO, "prot r8 [%06x] %02x @%06x", a, d, SekPc);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void PicoWrite8_plk3p(u32 a, u32 d)
|
||||
{
|
||||
elprintf(EL_UIO, "prot w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
|
||||
if (a & 2)
|
||||
prot_lk3_cmd = d;
|
||||
else
|
||||
prot_lk3_data = d;
|
||||
}
|
||||
|
||||
static void PicoWrite8_plk3b(u32 a, u32 d)
|
||||
{
|
||||
int addr;
|
||||
|
||||
elprintf(EL_UIO, "prot w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
|
||||
addr = d << 15;
|
||||
if (addr + 0x8000 > Pico.romsize) {
|
||||
elprintf(EL_UIO|EL_ANOMALY, "prot_lk3: bank too large: %02x", d);
|
||||
return;
|
||||
}
|
||||
if (addr == 0)
|
||||
memcpy(Pico.rom, Pico.rom + Pico.romsize, 0x8000);
|
||||
else
|
||||
memcpy(Pico.rom, Pico.rom + addr, 0x8000);
|
||||
}
|
||||
|
||||
static void carthw_prot_lk3_mem_setup(void)
|
||||
{
|
||||
cpu68k_map_set(m68k_read8_map, 0x600000, 0x7fffff, PicoRead8_plk3, 1);
|
||||
cpu68k_map_set(m68k_write8_map, 0x600000, 0x6fffff, PicoWrite8_plk3p, 1);
|
||||
cpu68k_map_set(m68k_write8_map, 0x700000, 0x7fffff, PicoWrite8_plk3b, 1);
|
||||
}
|
||||
|
||||
void carthw_prot_lk3_startup(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
elprintf(EL_STATUS, "lk3 prot emu startup");
|
||||
|
||||
// allocate space for bank0 backup
|
||||
ret = PicoCartResize(Pico.romsize + 0x8000);
|
||||
if (ret != 0) {
|
||||
elprintf(EL_STATUS, "OOM");
|
||||
return;
|
||||
}
|
||||
memcpy(Pico.rom + Pico.romsize, Pico.rom, 0x8000);
|
||||
|
||||
PicoCartMemSetup = carthw_prot_lk3_mem_setup;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
/* svp */
|
||||
#include "svp/ssp16.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char iram_rom[0x20000]; // IRAM (0-0x7ff) and program ROM (0x800-0x1ffff)
|
||||
unsigned char dram[0x20000];
|
||||
ssp1601_t ssp1601;
|
||||
} svp_t;
|
||||
|
||||
extern svp_t *svp;
|
||||
|
||||
void PicoSVPInit(void);
|
||||
void PicoSVPStartup(void);
|
||||
void PicoSVPMemSetup(void);
|
||||
|
||||
/* misc */
|
||||
void carthw_ssf2_startup(void);
|
||||
void carthw_Xin1_startup(void);
|
||||
void carthw_realtec_startup(void);
|
||||
void carthw_radica_startup(void);
|
||||
void carthw_pier_startup(void);
|
||||
|
||||
void carthw_sprot_startup(void);
|
||||
void carthw_sprot_new_location(unsigned int a,
|
||||
unsigned int mask, unsigned short val, int is_ro);
|
||||
|
||||
void carthw_prot_lk3_startup(void);
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
vscroll: 1 (0); 209 (26) - alternates every 4 frames
|
||||
vram range for patterns: 0000-999f (low scr 0000-395f,72e0-999f; high 3980-999f)
|
||||
name table address: c000
|
||||
seen DMAs (in order): [300002-3026c3]->[0020-26e1] len 4961
|
||||
[3026c2-303943]->[26e0-3961] len 2369
|
||||
[303942-306003]->[72e0-99a1] len 4961
|
||||
---
|
||||
[306002-3086c3]->[3980-6041] len 4961
|
||||
[3086c2-309943]->[6040-72c1] len 2369
|
||||
[309942-30c003]->[72e0-99a2] len 4961
|
||||
tile arrangement:
|
||||
|
||||
000: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
||||
001: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
||||
002: 001 003 005 007 009 00b 00d 00f 011 013 015 017 019 01b 01d 01f 021 023 025 027 029 02b 02d 02f 031 033 035 037 039 03b 03d 03f
|
||||
003: 002 004 006 008 00a 00c 00e 010 012 014 016 018 01a 01c 01e 020 022 024 026 028 02a 02c 02e 030 032 034 036 038 03a 03c 03e 040
|
||||
004: 041 043 045 047 049 04b 04d 04f 051 053 055 057 059 05b 05d 05f 061 063 065 067 069 06b 06d 06f 071 073 075 077 079 07b 07d 07f
|
||||
005: 042 044 046 048 04a 04c 04e 050 052 054 056 058 05a 05c 05e 060 062 064 066 068 06a 06c 06e 070 072 074 076 078 07a 07c 07e 080
|
||||
006: 081 083 085 087 089 08b 08d 08f 091 093 095 097 099 09b 09d 09f 0a1 0a3 0a5 0a7 0a9 0ab 0ad 0af 0b1 0b3 0b5 0b7 0b9 0bb 0bd 0bf
|
||||
007: 082 084 086 088 08a 08c 08e 090 092 094 096 098 09a 09c 09e 0a0 0a2 0a4 0a6 0a8 0aa 0ac 0ae 0b0 0b2 0b4 0b6 0b8 0ba 0bc 0be 0c0
|
||||
008: 0c1 0c3 0c5 0c7 0c9 0cb 0cd 0cf 0d1 0d3 0d5 0d7 0d9 0db 0dd 0df 0e1 0e3 0e5 0e7 0e9 0eb 0ed 0ef 0f1 0f3 0f5 0f7 0f9 0fb 0fd 0ff
|
||||
009: 0c2 0c4 0c6 0c8 0ca 0cc 0ce 0d0 0d2 0d4 0d6 0d8 0da 0dc 0de 0e0 0e2 0e4 0e6 0e8 0ea 0ec 0ee 0f0 0f2 0f4 0f6 0f8 0fa 0fc 0fe 100
|
||||
010: 101 103 105 107 109 10b 10d 10f 111 113 115 117 119 11b 11d 11f 121 123 125 127 129 12b 12d 12f 131 133 135 137 139 13b 13d 13f
|
||||
011: 102 104 106 108 10a 10c 10e 110 112 114 116 118 11a 11c 11e 120 122 124 126 128 12a 12c 12e 130 132 134 136 138 13a 13c 13e 140
|
||||
012: 141 143 145 147 149 14b 14d 14f 151 153 155 157 159 15b 15d 15f 161 163 165 167 169 16b 16d 16f 171 173 175 177 179 17b 17d 17f
|
||||
013: 142 144 146 148 14a 14c 14e 150 152 154 156 158 15a 15c 15e 160 162 164 166 168 16a 16c 16e 170 172 174 176 178 17a 17c 17e 180
|
||||
014: 181 183 185 187 189 18b 18d 18f 191 193 195 197 199 19b 19d 19f 1a1 1a3 1a5 1a7 1a9 1ab 1ad 1af 1b1 1b3 1b5 1b7 1b9 1bb 1bd 1bf
|
||||
015: 182 184 186 188 18a 18c 18e 190 192 194 196 198 19a 19c 19e 1a0 1a2 1a4 1a6 1a8 1aa 1ac 1ae 1b0 1b2 1b4 1b6 1b8 1ba 1bc 1be 1c0
|
||||
016: 1c1 1c3 1c5 1c7 1c9 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
|
||||
017: 1c2 1c4 1c6 1c8 1ca 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
|
||||
018: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
|
||||
019: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
|
||||
020: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
|
||||
021: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
|
||||
022: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
|
||||
023: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
|
||||
024: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
|
||||
025: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
|
||||
026: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
||||
027: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
||||
028: 1cc 1ce 1d0 1d2 1d4 1d6 1d8 1da 1dc 1de 1e0 1e2 1e4 1e6 1e8 1ea 1ec 1ee 1f0 1f2 1f4 1f6 1f8 1fa 1fc 1fe 200 202 204 206 208 20a
|
||||
029: 1cd 1cf 1d1 1d3 1d5 1d7 1d9 1db 1dd 1df 1e1 1e3 1e5 1e7 1e9 1eb 1ed 1ef 1f1 1f3 1f5 1f7 1f9 1fb 1fd 1ff 201 203 205 207 209 20b
|
||||
030: 20c 20e 210 212 214 216 218 21a 21c 21e 220 222 224 226 228 22a 22c 22e 230 232 234 236 238 23a 23c 23e 240 242 244 246 248 24a
|
||||
031: 20d 20f 211 213 215 217 219 21b 21d 21f 221 223 225 227 229 22b 22d 22f 231 233 235 237 239 23b 23d 23f 241 243 245 247 249 24b
|
||||
032: 24c 24e 250 252 254 256 258 25a 25c 25e 260 262 264 266 268 26a 26c 26e 270 272 274 276 278 27a 27c 27e 280 282 284 286 288 28a
|
||||
033: 24d 24f 251 253 255 257 259 25b 25d 25f 261 263 265 267 269 26b 26d 26f 271 273 275 277 279 27b 27d 27f 281 283 285 287 289 28b
|
||||
034: 28c 28e 290 292 294 296 298 29a 29c 29e 2a0 2a2 2a4 2a6 2a8 2aa 2ac 2ae 2b0 2b2 2b4 2b6 2b8 2ba 2bc 2be 2c0 2c2 2c4 2c6 2c8 2ca
|
||||
035: 28d 28f 291 293 295 297 299 29b 29d 29f 2a1 2a3 2a5 2a7 2a9 2ab 2ad 2af 2b1 2b3 2b5 2b7 2b9 2bb 2bd 2bf 2c1 2c3 2c5 2c7 2c9 2cb
|
||||
036: 2cc 2ce 2d0 2d2 2d4 2d6 2d8 2da 2dc 2de 2e0 2e2 2e4 2e6 2e8 2ea 2ec 2ee 2f0 2f2 2f4 2f6 2f8 2fa 2fc 2fe 300 302 304 306 308 30a
|
||||
037: 2cd 2cf 2d1 2d3 2d5 2d7 2d9 2db 2dd 2df 2e1 2e3 2e5 2e7 2e9 2eb 2ed 2ef 2f1 2f3 2f5 2f7 2f9 2fb 2fd 2ff 301 303 305 307 309 30b
|
||||
038: 30c 30e 310 312 314 316 318 31a 31c 31e 320 322 324 326 328 32a 32c 32e 330 332 334 336 338 33a 33c 33e 340 342 344 346 348 34a
|
||||
039: 30d 30f 311 313 315 317 319 31b 31d 31f 321 323 325 327 329 32b 32d 32f 331 333 335 337 339 33b 33d 33f 341 343 345 347 349 34b
|
||||
040: 34c 34e 350 352 354 356 358 35a 35c 35e 360 362 364 366 368 36a 36c 36e 370 372 374 376 378 37a 37c 37e 380 382 384 386 388 38a
|
||||
041: 34d 34f 351 353 355 357 359 35b 35d 35f 361 363 365 367 369 36b 36d 36f 371 373 375 377 379 37b 37d 37f 381 383 385 387 389 38b
|
||||
042: 38c 38e 390 392 394 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
|
||||
043: 38d 38f 391 393 395 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
|
||||
044: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
|
||||
045: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
|
||||
046: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
|
||||
047: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
|
||||
048: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
|
||||
049: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
|
||||
050: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
|
||||
051: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
|
||||
052: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
||||
053: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* The SVP chip emulator, mem I/O stuff
|
||||
*
|
||||
* Copyright (c) Gražvydas "notaz" Ignotas, 2008
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../../pico_int.h"
|
||||
#include "../../memory.h"
|
||||
|
||||
// for wait loop det
|
||||
static void PicoWrite16_dram(u32 a, u32 d)
|
||||
{
|
||||
a &= ~0xfe0000;
|
||||
|
||||
if (d != 0) {
|
||||
if (a == 0xfe06) // 30fe06
|
||||
svp->ssp1601.emu_status &= ~SSP_WAIT_30FE06;
|
||||
else if (a == 0xfe08)
|
||||
svp->ssp1601.emu_status &= ~SSP_WAIT_30FE08;
|
||||
}
|
||||
|
||||
((u16 *)svp->dram)[a / 2] = d;
|
||||
}
|
||||
|
||||
// "cell arrange" 1: 390000-39ffff
|
||||
static u32 PicoRead16_svpca1(u32 a)
|
||||
{
|
||||
// this is 68k code rewritten
|
||||
u32 a1 = a >> 1;
|
||||
a1 = (a1 & 0x7001) | ((a1 & 0x3e) << 6) | ((a1 & 0xfc0) >> 5);
|
||||
return ((u16 *)svp->dram)[a1];
|
||||
}
|
||||
|
||||
// "cell arrange" 2: 3a0000-3affff
|
||||
static u32 PicoRead16_svpca2(u32 a)
|
||||
{
|
||||
u32 a1 = a >> 1;
|
||||
a1 = (a1 & 0x7801) | ((a1 & 0x1e) << 6) | ((a1 & 0x7e0) >> 4);
|
||||
return ((u16 *)svp->dram)[a1];
|
||||
}
|
||||
|
||||
// IO/control area (0xa10000 - 0xa1ffff)
|
||||
static u32 PicoRead16_svpr(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
// regs
|
||||
if ((a & ~0x0f) == 0xa15000) {
|
||||
switch (a & 0xf) {
|
||||
case 0:
|
||||
case 2:
|
||||
d = svp->ssp1601.gr[SSP_XST].h;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
d = svp->ssp1601.gr[SSP_PM0].h;
|
||||
svp->ssp1601.gr[SSP_PM0].h &= ~1;
|
||||
break;
|
||||
}
|
||||
|
||||
#if EL_LOGMASK & EL_SVP
|
||||
{
|
||||
static int a15004_looping = 0;
|
||||
if (a == 0xa15004 && (d & 1))
|
||||
a15004_looping = 0;
|
||||
|
||||
if (!a15004_looping)
|
||||
elprintf(EL_SVP, "SVP r%i: [%06x] %04x @%06x", realsize, a, d, SekPc);
|
||||
|
||||
if (a == 0xa15004 && !(d&1)) {
|
||||
if (!a15004_looping)
|
||||
elprintf(EL_SVP, "SVP det TIGHT loop: a15004");
|
||||
a15004_looping = 1;
|
||||
}
|
||||
else
|
||||
a15004_looping = 0;
|
||||
}
|
||||
#endif
|
||||
return d;
|
||||
}
|
||||
|
||||
//if (a == 0x30fe02 && d == 0)
|
||||
// elprintf(EL_ANOMALY, "SVP lag?");
|
||||
|
||||
return PicoRead16_io(a);
|
||||
}
|
||||
|
||||
// used in VR test mode
|
||||
static u32 PicoRead8_svpr(u32 a)
|
||||
{
|
||||
u32 d;
|
||||
|
||||
if ((a & ~0x0f) != 0xa15000)
|
||||
return PicoRead8_io(a);
|
||||
|
||||
d = PicoRead16_svpr(a & ~1);
|
||||
if (!(a & 1))
|
||||
d >>= 8;
|
||||
return d;
|
||||
}
|
||||
|
||||
static void PicoWrite16_svpr(u32 a, u32 d)
|
||||
{
|
||||
elprintf(EL_SVP, "SVP w16: [%06x] %04x @%06x", a, d, SekPc);
|
||||
|
||||
if ((a & ~0x0f) == 0xa15000) {
|
||||
if (a == 0xa15000 || a == 0xa15002) {
|
||||
// just guessing here
|
||||
svp->ssp1601.gr[SSP_XST].h = d;
|
||||
svp->ssp1601.gr[SSP_PM0].h |= 2;
|
||||
svp->ssp1601.emu_status &= ~SSP_WAIT_PM0;
|
||||
}
|
||||
//else if (a == 0xa15006) svp->ssp1601.gr[SSP_PM0].h = d | (d << 1);
|
||||
// 0xa15006 probably has 'halt'
|
||||
return;
|
||||
}
|
||||
|
||||
PicoWrite16_io(a, d);
|
||||
}
|
||||
|
||||
void PicoSVPMemSetup(void)
|
||||
{
|
||||
// 68k memmap:
|
||||
// DRAM
|
||||
cpu68k_map_set(m68k_read8_map, 0x300000, 0x31ffff, svp->dram, 0);
|
||||
cpu68k_map_set(m68k_read16_map, 0x300000, 0x31ffff, svp->dram, 0);
|
||||
cpu68k_map_set(m68k_write8_map, 0x300000, 0x31ffff, svp->dram, 0);
|
||||
cpu68k_map_set(m68k_write16_map, 0x300000, 0x31ffff, svp->dram, 0);
|
||||
cpu68k_map_set(m68k_write16_map, 0x300000, 0x30ffff, PicoWrite16_dram, 1);
|
||||
|
||||
// DRAM (cell arrange)
|
||||
cpu68k_map_set(m68k_read16_map, 0x390000, 0x39ffff, PicoRead16_svpca1, 1);
|
||||
cpu68k_map_set(m68k_read16_map, 0x3a0000, 0x3affff, PicoRead16_svpca2, 1);
|
||||
|
||||
// regs
|
||||
cpu68k_map_set(m68k_read8_map, 0xa10000, 0xa1ffff, PicoRead8_svpr, 1);
|
||||
cpu68k_map_set(m68k_read16_map, 0xa10000, 0xa1ffff, PicoRead16_svpr, 1);
|
||||
cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, PicoWrite8_io, 1); // PicoWrite8_svpr
|
||||
cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, PicoWrite16_svpr, 1);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* basic, incomplete SSP160x (SSP1601?) interpreter
|
||||
*
|
||||
* Copyright (c) Gražvydas "notaz" Ignotas, 2008
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// register names
|
||||
enum {
|
||||
SSP_GR0, SSP_X, SSP_Y, SSP_A,
|
||||
SSP_ST, SSP_STACK, SSP_PC, SSP_P,
|
||||
SSP_PM0, SSP_PM1, SSP_PM2, SSP_XST,
|
||||
SSP_PM4, SSP_gr13, SSP_PMC, SSP_AL
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned int v;
|
||||
struct {
|
||||
unsigned short l;
|
||||
unsigned short h;
|
||||
};
|
||||
} ssp_reg_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
unsigned short RAM[256*2]; // 000 2 internal RAM banks
|
||||
struct {
|
||||
unsigned short RAM0[256];
|
||||
unsigned short RAM1[256];
|
||||
};
|
||||
};
|
||||
ssp_reg_t gr[16]; // 400 general registers
|
||||
union {
|
||||
unsigned char r[8]; // 440 BANK pointers
|
||||
struct {
|
||||
unsigned char r0[4];
|
||||
unsigned char r1[4];
|
||||
};
|
||||
};
|
||||
unsigned short stack[6]; // 448
|
||||
unsigned int pmac_read[6]; // 454 read modes/addrs for PM0-PM5
|
||||
unsigned int pmac_write[6]; // 46c write ...
|
||||
//
|
||||
#define SSP_PMC_HAVE_ADDR 0x0001 // address written to PMAC, waiting for mode
|
||||
#define SSP_PMC_SET 0x0002 // PMAC is set
|
||||
#define SSP_WAIT_PM0 0x2000 // bit1 in PM0
|
||||
#define SSP_WAIT_30FE06 0x4000 // ssp tight loops on 30FE06 to become non-zero
|
||||
#define SSP_WAIT_30FE08 0x8000 // same for 30FE06
|
||||
#define SSP_WAIT_MASK 0xe000
|
||||
unsigned int emu_status; // 484
|
||||
/* used by recompiler only: */
|
||||
struct {
|
||||
unsigned int ptr_rom; // 488
|
||||
unsigned int ptr_iram_rom; // 48c
|
||||
unsigned int ptr_dram; // 490
|
||||
unsigned int iram_dirty; // 494
|
||||
unsigned int iram_context; // 498
|
||||
unsigned int ptr_btable; // 49c
|
||||
unsigned int ptr_btable_iram; // 4a0
|
||||
unsigned int tmp0; // 4a4
|
||||
unsigned int tmp1; // 4a8
|
||||
unsigned int tmp2; // 4ac
|
||||
} drc;
|
||||
} ssp1601_t;
|
||||
|
||||
|
||||
void ssp1601_reset(ssp1601_t *ssp);
|
||||
void ssp1601_run(int cycles);
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* The SVP chip emulator
|
||||
*
|
||||
* Copyright (c) Gražvydas "notaz" Ignotas, 2008
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <pico/pico_int.h>
|
||||
|
||||
svp_t *svp = NULL;
|
||||
int PicoSVPCycles = 850; // cycles/line, just a guess
|
||||
static int svp_dyn_ready = 0;
|
||||
|
||||
static void PicoSVPReset(void)
|
||||
{
|
||||
elprintf(EL_SVP, "SVP reset");
|
||||
|
||||
memcpy(svp->iram_rom + 0x800, Pico.rom + 0x800, 0x20000 - 0x800);
|
||||
ssp1601_reset(&svp->ssp1601);
|
||||
#ifdef _SVP_DRC
|
||||
if ((PicoOpt & POPT_EN_DRC) && svp_dyn_ready)
|
||||
ssp1601_dyn_reset(&svp->ssp1601);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void PicoSVPLine(void)
|
||||
{
|
||||
int count = 1;
|
||||
#if defined(__arm__) || defined(PSP)
|
||||
// performance hack
|
||||
static int delay_lines = 0;
|
||||
delay_lines++;
|
||||
if ((Pico.m.scanline&0xf) != 0xf && Pico.m.scanline != 261 && Pico.m.scanline != 311)
|
||||
return;
|
||||
count = delay_lines;
|
||||
delay_lines = 0;
|
||||
#endif
|
||||
|
||||
#ifdef _SVP_DRC
|
||||
if ((PicoOpt & POPT_EN_DRC) && svp_dyn_ready)
|
||||
ssp1601_dyn_run(PicoSVPCycles * count);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ssp1601_run(PicoSVPCycles * count);
|
||||
svp_dyn_ready = 0; // just in case
|
||||
}
|
||||
|
||||
// test mode
|
||||
//if (Pico.m.frame_count == 13) PicoPad[0] |= 0xff;
|
||||
}
|
||||
|
||||
|
||||
static int PicoSVPDma(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp)
|
||||
{
|
||||
if (source < Pico.romsize) // Rom
|
||||
{
|
||||
source -= 2;
|
||||
*srcp = (unsigned short *)(Pico.rom + (source&~1));
|
||||
*limitp = (unsigned short *)(Pico.rom + Pico.romsize);
|
||||
return 1;
|
||||
}
|
||||
else if ((source & 0xfe0000) == 0x300000)
|
||||
{
|
||||
elprintf(EL_VDPDMA|EL_SVP, "SVP DmaSlow from %06x, len=%i", source, len);
|
||||
source &= 0x1fffe;
|
||||
source -= 2;
|
||||
*srcp = (unsigned short *)(svp->dram + source);
|
||||
*limitp = (unsigned short *)(svp->dram + sizeof(svp->dram));
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
elprintf(EL_VDPDMA|EL_SVP|EL_ANOMALY, "SVP FIXME unhandled DmaSlow from %06x, len=%i", source, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PicoSVPInit(void)
|
||||
{
|
||||
#ifdef _SVP_DRC
|
||||
// this is to unmap tcache and make
|
||||
// mem available for large ROMs, MCD, etc.
|
||||
drc_cmn_cleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PicoSVPExit(void)
|
||||
{
|
||||
#ifdef _SVP_DRC
|
||||
ssp1601_dyn_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PicoSVPStartup(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
elprintf(EL_STATUS, "SVP startup");
|
||||
|
||||
ret = PicoCartResize(Pico.romsize + sizeof(*svp));
|
||||
if (ret != 0) {
|
||||
elprintf(EL_STATUS|EL_SVP, "OOM for SVP data");
|
||||
return;
|
||||
}
|
||||
|
||||
svp = (void *) ((char *)Pico.rom + Pico.romsize);
|
||||
memset(svp, 0, sizeof(*svp));
|
||||
|
||||
// init SVP compiler
|
||||
svp_dyn_ready = 0;
|
||||
#ifdef _SVP_DRC
|
||||
if (PicoOpt & POPT_EN_DRC) {
|
||||
if (ssp1601_dyn_startup())
|
||||
return;
|
||||
svp_dyn_ready = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// init ok, setup hooks..
|
||||
PicoCartMemSetup = PicoSVPMemSetup;
|
||||
PicoDmaHook = PicoSVPDma;
|
||||
PicoResetHook = PicoSVPReset;
|
||||
PicoLineHook = PicoSVPLine;
|
||||
PicoAHW |= PAHW_SVP;
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/* generated by ./tools/make_carthw_c, do not modify */
|
||||
static const char builtin_carthw_cfg[] =
|
||||
"[]\n"
|
||||
"check_str=0x150,\"Virtua Racing\"\n"
|
||||
"check_str=0x810,\"OHMP\"\n"
|
||||
"hw=svp\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"VIRTUA RACING\"\n"
|
||||
"check_str=0x810,\"OHMP\"\n"
|
||||
"hw=svp\n"
|
||||
"[]\n"
|
||||
"check_str=0x100,\"SEGA PICO\"\n"
|
||||
"hw=pico\n"
|
||||
"[]\n"
|
||||
"check_str=0x100,\"IMA IKUNOUJYUKU\"\n"
|
||||
"hw=pico\n"
|
||||
"[]\n"
|
||||
"check_str=0x120,\"PUGGSY\"\n"
|
||||
"prop=no_sram\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"DINO DINI'S SOCCER\"\n"
|
||||
"prop=filled_sram\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"MICRO MACHINES II\"\n"
|
||||
"prop=filled_sram\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"32X SAMPLE PROGRAM\"\n"
|
||||
"check_str=0x32b74c,\"Bishop Level\"\n"
|
||||
"prop=force_6btn\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"SUPER STREET FIGHTER2 The New Challengers\"\n"
|
||||
"hw=ssf2_mapper\n"
|
||||
"prop=no_sram\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"PIER\"\n"
|
||||
"check_str=0x610,\"Respect\"\n"
|
||||
"hw=piersolar_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x120,\"FLICKY\"\n"
|
||||
"check_size_gt=0x020000\n"
|
||||
"hw=x_in_1_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"ROBOCOP 3\"\n"
|
||||
"check_size_gt=0x080000\n"
|
||||
"hw=x_in_1_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"ALIEN 3\"\n"
|
||||
"check_size_gt=0x080000\n"
|
||||
"hw=x_in_1_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\" SHOVE IT!\"\n"
|
||||
"check_size_gt=0x020000\n"
|
||||
"hw=x_in_1_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"MS PACMAN\"\n"
|
||||
"check_size_gt=0x020000\n"
|
||||
"hw=x_in_1_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"KID CHAMELEON\"\n"
|
||||
"check_size_gt=0x100000\n"
|
||||
"hw=radica_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x94,\"THE EARTH DEFEND\"\n"
|
||||
"hw=realtec_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0xfe,\"WISEGAME 11-03-1993\"\n"
|
||||
"hw=realtec_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x95,\"MALLET LEGEND\"\n"
|
||||
"hw=realtec_mapper\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"COLLEGE SLAM\"\n"
|
||||
"eeprom_type=3\n"
|
||||
"eeprom_lines=8,0,0\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"FRANK THOMAS BIGHURT BASEBAL\"\n"
|
||||
"eeprom_type=3\n"
|
||||
"eeprom_lines=8,0,0\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"MICRO MACHINES II\"\n"
|
||||
"sram_range=0x300000,0x380001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=9,8,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\" \"\n"
|
||||
"check_csum=0x165e\n"
|
||||
"sram_range=0x300000,0x380001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=9,8,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\" \"\n"
|
||||
"check_csum=0x2c41\n"
|
||||
"sram_range=0x300000,0x380001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=9,8,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\" \"\n"
|
||||
"check_csum=0x168b\n"
|
||||
"sram_range=0x300000,0x380001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=9,8,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\" \"\n"
|
||||
"check_csum=0xcee0\n"
|
||||
"sram_range=0x300000,0x380001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=9,8,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"NBA JAM \"\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=1,0,1\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"NBA JAM TOURNAMENT EDITION\"\n"
|
||||
"sram_range=0x200000,0x200001\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=8,0,0\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"NFL QUARTERBACK CLUB\"\n"
|
||||
"eeprom_type=2\n"
|
||||
"eeprom_lines=8,0,0\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"NHLPA Hockey '93\"\n"
|
||||
"sram_range=0x200000,0x200001\n"
|
||||
"eeprom_type=1\n"
|
||||
"eeprom_lines=6,7,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"NHLPA HOCKEY '93\"\n"
|
||||
"sram_range=0x200000,0x200001\n"
|
||||
"eeprom_type=1\n"
|
||||
"eeprom_lines=6,7,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x150,\"RINGS OF POWER\"\n"
|
||||
"sram_range=0x200000,0x200001\n"
|
||||
"eeprom_type=1\n"
|
||||
"eeprom_lines=6,7,7\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" \"\n"
|
||||
"check_crc32=0x10458e09\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13000,0xffff00,0x28\n"
|
||||
"[]\n"
|
||||
"check_str=0x172,\"GAME : ELF WOR\"\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x400000,-2,0x5500\n"
|
||||
"prot_ro_value16=0x400002,-2,0xc900#checkisdoneiftheaboveonefails\n"
|
||||
"prot_ro_value16=0x400004,-2,0x0f00\n"
|
||||
"prot_ro_value16=0x400006,-2,0x1800#similartoabove\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" \"\n"
|
||||
"check_crc32=0xcbc38eea\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x480000,0xff0000,0xaa00\n"
|
||||
"prot_ro_value16=0x4a0000,0xff0000,0x0a00\n"
|
||||
"prot_ro_value16=0x4c0000,0xff0000,0xf000\n"
|
||||
"prot_ro_value16=0x400000,0xc00000,0x0000#defaultfor400000-7f0000\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0xc004219d\n"
|
||||
"hw=prot_lk3\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0xaff46765\n"
|
||||
"hw=prot\n"
|
||||
"prot_rw_value16=0x400000,0xc00004,0\n"
|
||||
"prot_rw_value16=0x400004,0xc00004,0\n"
|
||||
"[]\n"
|
||||
"check_str=0x118,\"CREATON. \"\n"
|
||||
"check_crc32=0xddd02ba4\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x400000,-2,0x9000\n"
|
||||
"prot_ro_value16=0x401000,-2,0xd300\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" \"\n"
|
||||
"check_crc32=0xf68f6367\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13002,-2,0x01\n"
|
||||
"prot_ro_value16=0xa1303e,-2,0x1f\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" \"\n"
|
||||
"check_crc32=0xfb176667\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13000,-2,0x14\n"
|
||||
"prot_ro_value16=0xa13002,-2,0x01\n"
|
||||
"prot_ro_value16=0xa1303e,-2,0x1f\n"
|
||||
"[]\n"
|
||||
"check_csum=0\n"
|
||||
"check_crc32=0x3ee639f0\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13000,-2,0x0c\n"
|
||||
"[]\n"
|
||||
"check_csum=0\n"
|
||||
"check_crc32=0xdecdf740\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x400000,-2,0x5500\n"
|
||||
"prot_ro_value16=0x400002,-2,0x0f00\n"
|
||||
"prot_ro_value16=0x400004,-2,0xaa00\n"
|
||||
"prot_ro_value16=0x400006,-2,0xf000\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" \"\n"
|
||||
"check_crc32=0xf26f88d1\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x400002,-2,0x9800\n"
|
||||
"prot_ro_value16=0x400004,-2,0xaa00#or0xc900\n"
|
||||
"prot_ro_value16=0x400006,-2,0xf000\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0x4820a161\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0x400000,-2,0x5500\n"
|
||||
"prot_ro_value16=0x400002,-2,0x0f00\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0x413dfee2\n"
|
||||
"hw=prot_lk3\n"
|
||||
"[]\n"
|
||||
"check_str=0x140,\"SUPER MARIO BROS \"\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13000,-2,0x0c\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0xf7e1b3e1\n"
|
||||
"hw=prot\n"
|
||||
"prot_ro_value16=0xa13000,-2,0x0a\n"
|
||||
"[]\n"
|
||||
"check_str=0x104,\" are Registered Trademarks\"\n"
|
||||
"check_crc32=0xb8261ff5\n"
|
||||
"hw=prot\n"
|
||||
"prot_rw_value16=0x400000,0xc00000,0\n"
|
||||
;
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* CD image handler
|
||||
* (C) notaz, 2007,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
#include "genplus_macros.h"
|
||||
#include "cdd.h"
|
||||
#include "cue.h"
|
||||
|
||||
static int handle_mp3(const char *fname, int index)
|
||||
{
|
||||
track_t *track = &cdd.toc.tracks[index];
|
||||
FILE *tmp_file;
|
||||
int kBps;
|
||||
int fs, ret;
|
||||
|
||||
tmp_file = fopen(fname, "rb");
|
||||
if (tmp_file == NULL)
|
||||
return -1;
|
||||
|
||||
ret = fseek(tmp_file, 0, SEEK_END);
|
||||
fs = ftell(tmp_file);
|
||||
fseek(tmp_file, 0, SEEK_SET);
|
||||
|
||||
#ifdef _PSP_FW_VERSION
|
||||
// some systems (like PSP) can't have many open files at a time,
|
||||
// so we work with their names instead.
|
||||
fclose(tmp_file);
|
||||
tmp_file = (void *) strdup(fname);
|
||||
#endif
|
||||
|
||||
kBps = mp3_get_bitrate(tmp_file, fs) / 8;
|
||||
if (ret != 0 || kBps <= 0)
|
||||
{
|
||||
elprintf(EL_STATUS, "track %2i: mp3 bitrate %i", index+1, kBps);
|
||||
#ifdef _PSP_FW_VERSION
|
||||
free(tmp_file);
|
||||
#else
|
||||
fclose(tmp_file);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
track->fd = tmp_file;
|
||||
track->offset = 0;
|
||||
|
||||
fs *= 75;
|
||||
fs /= kBps * 1000;
|
||||
return fs;
|
||||
}
|
||||
|
||||
static void to_upper(char *d, const char *s)
|
||||
{
|
||||
for (; *s != 0; d++, s++) {
|
||||
if ('a' <= *s && *s <= 'z')
|
||||
*d = *s - 'a' + 'A';
|
||||
else
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
// cdd.c uses lba - 150
|
||||
static void sprintf_lba(char *buf, size_t size, int lba)
|
||||
{
|
||||
lba += 150;
|
||||
snprintf(buf, size, "%02d:%02d:%02d", lba / 60 / 75,
|
||||
(lba / 75) % 60, lba % 75);
|
||||
}
|
||||
|
||||
int load_cd_image(const char *cd_img_name, int *type)
|
||||
{
|
||||
static const char *exts[] = {
|
||||
"%02d.mp3", " %02d.mp3", "-%02d.mp3", "_%02d.mp3", " - %02d.mp3",
|
||||
"%d.mp3", " %d.mp3", "-%d.mp3", "_%d.mp3", " - %d.mp3",
|
||||
};
|
||||
int i, j, n, lba, index, length, ret;
|
||||
int iso_name_len, missed, cd_img_sectors;
|
||||
char tmp_name[256], tmp_ext[10], tmp_ext_u[10];
|
||||
track_t *tracks = cdd.toc.tracks;
|
||||
cue_data_t *cue_data = NULL;
|
||||
pm_file *pmf;
|
||||
|
||||
if (PicoCDLoadProgressCB != NULL)
|
||||
PicoCDLoadProgressCB(cd_img_name, 1);
|
||||
|
||||
Pico_mcd->cdda_type = CT_UNKNOWN;
|
||||
|
||||
/* is this a .cue? */
|
||||
cue_data = cue_parse(cd_img_name);
|
||||
if (cue_data != NULL) {
|
||||
cd_img_name = cue_data->tracks[1].fname;
|
||||
*type = cue_data->tracks[1].type;
|
||||
}
|
||||
|
||||
pmf = pm_open(cd_img_name);
|
||||
if (pmf == NULL)
|
||||
{
|
||||
if (cue_data != NULL)
|
||||
cue_destroy(cue_data);
|
||||
return -1;
|
||||
}
|
||||
tracks[0].fd = pmf;
|
||||
|
||||
if (*type == CT_ISO)
|
||||
cd_img_sectors = pmf->size >>= 11; // size in sectors
|
||||
else cd_img_sectors = pmf->size /= 2352;
|
||||
|
||||
// cdd.c operates with lba - 150
|
||||
tracks[0].start = 0;
|
||||
tracks[0].end = cd_img_sectors;
|
||||
tracks[0].offset = 0;
|
||||
|
||||
sprintf_lba(tmp_ext, sizeof(tmp_ext), 0);
|
||||
elprintf(EL_STATUS, "Track 1: %s %9i DATA %s",
|
||||
tmp_ext, tracks[0].end, cd_img_name);
|
||||
|
||||
lba = cd_img_sectors;
|
||||
|
||||
if (cue_data != NULL)
|
||||
{
|
||||
if (cue_data->tracks[2].fname == NULL) {
|
||||
// NULL fname means track2 is in same file as track1
|
||||
lba = tracks[0].end = cue_data->tracks[2].sector_offset;
|
||||
}
|
||||
i = 100 / cue_data->track_count + 1; // progress display
|
||||
|
||||
for (n = 2; n <= cue_data->track_count; n++)
|
||||
{
|
||||
if (PicoCDLoadProgressCB != NULL)
|
||||
PicoCDLoadProgressCB(cd_img_name, i * n);
|
||||
|
||||
index = n - 1;
|
||||
lba += cue_data->tracks[n].pregap;
|
||||
if (cue_data->tracks[n].type == CT_MP3) {
|
||||
ret = handle_mp3(cue_data->tracks[n].fname, index);
|
||||
if (ret < 0)
|
||||
break;
|
||||
length = ret;
|
||||
}
|
||||
else if (cue_data->tracks[n].fname != NULL)
|
||||
{
|
||||
pm_file *f = pm_open(cue_data->tracks[n].fname);
|
||||
if (f != NULL)
|
||||
{
|
||||
// assume raw, ignore header for wav..
|
||||
tracks[index].fd = f;
|
||||
tracks[index].offset = cue_data->tracks[n].sector_offset;
|
||||
length = f->size / 2352;
|
||||
}
|
||||
else
|
||||
{
|
||||
elprintf(EL_STATUS, "track %2i (%s): can't determine length",
|
||||
n, cue_data->tracks[n].fname);
|
||||
tracks[index].offset = 0;
|
||||
length = 2*75;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n < cue_data->track_count)
|
||||
length = cue_data->tracks[n+1].sector_offset -
|
||||
cue_data->tracks[n].sector_offset;
|
||||
else
|
||||
length = cd_img_sectors - cue_data->tracks[n].sector_offset;
|
||||
tracks[index].offset = cue_data->tracks[n].sector_offset;
|
||||
}
|
||||
|
||||
if (cue_data->tracks[n].sector_xlength != 0)
|
||||
// overriden by custom cue command
|
||||
length = cue_data->tracks[n].sector_xlength;
|
||||
|
||||
Pico_mcd->cdda_type = cue_data->tracks[n].type;
|
||||
|
||||
tracks[index].start = lba;
|
||||
lba += length;
|
||||
tracks[index].end = lba;
|
||||
|
||||
sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start);
|
||||
elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO %s",
|
||||
n, tmp_ext, length, cue_data->tracks[n].fname);
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* mp3 track autosearch, Gens-like */
|
||||
iso_name_len = strlen(cd_img_name);
|
||||
if (iso_name_len >= sizeof(tmp_name))
|
||||
iso_name_len = sizeof(tmp_name) - 1;
|
||||
|
||||
for (n = 2, i = 0, missed = 0; i < 100 && missed < 4; i++)
|
||||
{
|
||||
if (PicoCDLoadProgressCB != NULL && i > 1)
|
||||
PicoCDLoadProgressCB(cd_img_name, i + (100-i)*missed/4);
|
||||
|
||||
for (j = 0; j < sizeof(exts)/sizeof(char *); j++)
|
||||
{
|
||||
int ext_len;
|
||||
char *p;
|
||||
|
||||
index = n - 1;
|
||||
|
||||
snprintf(tmp_ext, sizeof(tmp_ext), exts[j], i);
|
||||
ext_len = strlen(tmp_ext);
|
||||
to_upper(tmp_ext_u, tmp_ext);
|
||||
|
||||
memcpy(tmp_name, cd_img_name, iso_name_len + 1);
|
||||
p = tmp_name + iso_name_len - 4;
|
||||
|
||||
strcpy(p, tmp_ext);
|
||||
ret = handle_mp3(tmp_name, index);
|
||||
if (ret <= 0) {
|
||||
strcpy(p, tmp_ext_u);
|
||||
ret = handle_mp3(tmp_name, index);
|
||||
}
|
||||
|
||||
if (ret <= 0 && i > 1 && iso_name_len > ext_len) {
|
||||
p = tmp_name + iso_name_len - ext_len;
|
||||
strcpy(p, tmp_ext);
|
||||
ret = handle_mp3(tmp_name, index);
|
||||
if (ret <= 0) {
|
||||
strcpy(p, tmp_ext_u);
|
||||
ret = handle_mp3(tmp_name, index);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
length = ret;
|
||||
tracks[index].start = lba;
|
||||
lba += length;
|
||||
tracks[index].end = lba;
|
||||
|
||||
Pico_mcd->cdda_type = CT_MP3;
|
||||
|
||||
sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start);
|
||||
elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO - %s",
|
||||
n, tmp_ext, length, tmp_name);
|
||||
|
||||
n++;
|
||||
missed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret <= 0 && i > 1)
|
||||
missed++;
|
||||
}
|
||||
|
||||
finish:
|
||||
cdd.toc.last = n - 1;
|
||||
cdd.toc.end = lba;
|
||||
|
||||
sprintf_lba(tmp_ext, sizeof(tmp_ext), cdd.toc.end);
|
||||
elprintf(EL_STATUS, "End CD - %s\n", tmp_ext);
|
||||
|
||||
if (PicoCDLoadProgressCB != NULL)
|
||||
PicoCDLoadProgressCB(cd_img_name, 100);
|
||||
|
||||
if (cue_data != NULL)
|
||||
cue_destroy(cue_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,846 @@
|
|||
/***************************************************************************************
|
||||
* Genesis Plus
|
||||
* CD data controller (LC89510 compatible)
|
||||
*
|
||||
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
|
||||
*
|
||||
* Redistribution and use of this code or any derivative works are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions may not be sold, nor may they be used in a commercial
|
||||
* product or activity.
|
||||
*
|
||||
* - Redistributions that are modified from the original source must include the
|
||||
* complete source code, including the source code for all components used by a
|
||||
* binary built from the modified sources. However, as a special exception, the
|
||||
* source code distributed need not include anything that is normally distributed
|
||||
* (in either source or binary form) with the major components (compiler, kernel,
|
||||
* and so on) of the operating system on which the executable runs, unless that
|
||||
* component itself accompanies the executable.
|
||||
*
|
||||
* - Redistributions must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "../pico_int.h"
|
||||
#include "genplus_macros.h"
|
||||
|
||||
/* IFSTAT register bitmasks */
|
||||
#define BIT_DTEI 0x40
|
||||
#define BIT_DECI 0x20
|
||||
#define BIT_DTBSY 0x08
|
||||
#define BIT_DTEN 0x02
|
||||
|
||||
/* IFCTRL register bitmasks */
|
||||
#define BIT_DTEIEN 0x40
|
||||
#define BIT_DECIEN 0x20
|
||||
#define BIT_DOUTEN 0x02
|
||||
|
||||
/* CTRL0 register bitmasks */
|
||||
#define BIT_DECEN 0x80
|
||||
#define BIT_E01RQ 0x20
|
||||
#define BIT_AUTORQ 0x10
|
||||
#define BIT_WRRQ 0x04
|
||||
|
||||
/* CTRL1 register bitmasks */
|
||||
#define BIT_MODRQ 0x08
|
||||
#define BIT_FORMRQ 0x04
|
||||
#define BIT_SHDREN 0x01
|
||||
|
||||
/* CTRL2 register bitmask */
|
||||
#define BIT_VALST 0x80
|
||||
|
||||
/* PicoDrive: doing DMA at once, not using callbacks */
|
||||
//#define DMA_BYTES_PER_LINE 512
|
||||
|
||||
enum dma_type {
|
||||
word_ram_0_dma_w = 1,
|
||||
word_ram_1_dma_w = 2,
|
||||
word_ram_2M_dma_w = 3,
|
||||
pcm_ram_dma_w = 4,
|
||||
prg_ram_dma_w = 5,
|
||||
};
|
||||
|
||||
/* CDC hardware */
|
||||
typedef struct
|
||||
{
|
||||
uint8 ifstat;
|
||||
uint8 ifctrl;
|
||||
uint16 dbc;
|
||||
uint16 dac;
|
||||
uint16 pt;
|
||||
uint16 wa;
|
||||
uint8 ctrl[2];
|
||||
uint8 head[2][4];
|
||||
uint8 stat[4];
|
||||
int cycles;
|
||||
//void (*dma_w)(unsigned int words);
|
||||
int dma_w;
|
||||
uint8 ram[0x4000 + 2352]; /* 16K external RAM (with one block overhead to handle buffer overrun) */
|
||||
} cdc_t;
|
||||
|
||||
static cdc_t cdc;
|
||||
|
||||
void cdc_init(void)
|
||||
{
|
||||
memset(&cdc, 0, sizeof(cdc_t));
|
||||
}
|
||||
|
||||
void cdc_reset(void)
|
||||
{
|
||||
/* reset CDC register index */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x00;
|
||||
|
||||
/* reset CDC registers */
|
||||
cdc.ifstat = 0xff;
|
||||
cdc.ifctrl = 0x00;
|
||||
cdc.ctrl[0] = 0x00;
|
||||
cdc.ctrl[1] = 0x00;
|
||||
cdc.stat[0] = 0x00;
|
||||
cdc.stat[1] = 0x00;
|
||||
cdc.stat[2] = 0x00;
|
||||
cdc.stat[3] = 0x80;
|
||||
cdc.head[0][0] = 0x00;
|
||||
cdc.head[0][1] = 0x00;
|
||||
cdc.head[0][2] = 0x00;
|
||||
cdc.head[0][3] = 0x01;
|
||||
cdc.head[1][0] = 0x00;
|
||||
cdc.head[1][1] = 0x00;
|
||||
cdc.head[1][2] = 0x00;
|
||||
cdc.head[1][3] = 0x00;
|
||||
|
||||
/* reset CDC cycle counter */
|
||||
cdc.cycles = 0;
|
||||
|
||||
/* DMA transfer disabled */
|
||||
cdc.dma_w = 0;
|
||||
}
|
||||
|
||||
int cdc_context_save(uint8 *state)
|
||||
{
|
||||
uint8 tmp8;
|
||||
int bufferptr = 0;
|
||||
|
||||
if (cdc.dma_w == pcm_ram_dma_w)
|
||||
{
|
||||
tmp8 = 1;
|
||||
}
|
||||
else if (cdc.dma_w == prg_ram_dma_w)
|
||||
{
|
||||
tmp8 = 2;
|
||||
}
|
||||
else if (cdc.dma_w == word_ram_0_dma_w)
|
||||
{
|
||||
tmp8 = 3;
|
||||
}
|
||||
else if (cdc.dma_w == word_ram_1_dma_w)
|
||||
{
|
||||
tmp8 = 4;
|
||||
}
|
||||
else if (cdc.dma_w == word_ram_2M_dma_w)
|
||||
{
|
||||
tmp8 = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp8 = 0;
|
||||
}
|
||||
|
||||
save_param(&cdc, sizeof(cdc));
|
||||
save_param(&tmp8, 1);
|
||||
|
||||
return bufferptr;
|
||||
}
|
||||
|
||||
int cdc_context_load(uint8 *state)
|
||||
{
|
||||
uint8 tmp8;
|
||||
int bufferptr = 0;
|
||||
|
||||
load_param(&cdc, sizeof(cdc));
|
||||
load_param(&tmp8, 1);
|
||||
|
||||
switch (tmp8)
|
||||
{
|
||||
case 1:
|
||||
cdc.dma_w = pcm_ram_dma_w;
|
||||
break;
|
||||
case 2:
|
||||
cdc.dma_w = prg_ram_dma_w;
|
||||
break;
|
||||
case 3:
|
||||
cdc.dma_w = word_ram_0_dma_w;
|
||||
break;
|
||||
case 4:
|
||||
cdc.dma_w = word_ram_1_dma_w;
|
||||
break;
|
||||
case 5:
|
||||
cdc.dma_w = word_ram_2M_dma_w;
|
||||
break;
|
||||
default:
|
||||
cdc.dma_w = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return bufferptr;
|
||||
}
|
||||
|
||||
int cdc_context_load_old(uint8 *state)
|
||||
{
|
||||
#define old_load(v, ofs) \
|
||||
memcpy(&cdc.v, state + ofs, sizeof(cdc.v))
|
||||
|
||||
memcpy(cdc.ram, state, 0x4000);
|
||||
old_load(ifstat, 67892);
|
||||
old_load(ifctrl, 67924);
|
||||
old_load(dbc, 67896);
|
||||
old_load(dac, 67900);
|
||||
old_load(pt, 67908);
|
||||
old_load(wa, 67912);
|
||||
old_load(ctrl, 67928);
|
||||
old_load(head[0], 67904);
|
||||
old_load(stat, 67916);
|
||||
|
||||
cdc.dma_w = 0;
|
||||
switch (Pico_mcd->s68k_regs[0x04+0] & 0x07)
|
||||
{
|
||||
case 4: /* PCM RAM DMA */
|
||||
cdc.dma_w = pcm_ram_dma_w;
|
||||
break;
|
||||
case 5: /* PRG-RAM DMA */
|
||||
cdc.dma_w = prg_ram_dma_w;
|
||||
break;
|
||||
case 7: /* WORD-RAM DMA */
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x04)
|
||||
{
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x01)
|
||||
cdc.dma_w = word_ram_0_dma_w;
|
||||
else
|
||||
cdc.dma_w = word_ram_1_dma_w;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x02)
|
||||
cdc.dma_w = word_ram_2M_dma_w;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0x10960; // sizeof(old_cdc)
|
||||
#undef old_load
|
||||
}
|
||||
|
||||
static void do_dma(enum dma_type type, int words_in)
|
||||
{
|
||||
int dma_addr = (Pico_mcd->s68k_regs[0x0a] << 8) | Pico_mcd->s68k_regs[0x0b];
|
||||
int src_addr = cdc.dac & 0x3ffe;
|
||||
int dst_addr = dma_addr;
|
||||
int words = words_in;
|
||||
int dst_limit = 0;
|
||||
uint8 *dst;
|
||||
int len;
|
||||
|
||||
elprintf(EL_CD, "dma %d %04x->%04x %x",
|
||||
type, cdc.dac, dst_addr, words_in);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case pcm_ram_dma_w:
|
||||
dst_addr = (dst_addr << 2) & 0xffc;
|
||||
if (dst_addr + words * 2 > 0x1000) {
|
||||
elprintf(EL_ANOMALY, "pcm dma oflow: %x %x", dst_addr, words);
|
||||
words = (0x1000 - dst_addr) / 2;
|
||||
}
|
||||
dst = Pico_mcd->pcm_ram_b[Pico_mcd->pcm.bank];
|
||||
dst = dst + dst_addr;
|
||||
while (words > 0)
|
||||
{
|
||||
if (src_addr + words * 2 > 0x4000) {
|
||||
len = 0x4000 - src_addr;
|
||||
memcpy(dst, cdc.ram + src_addr, len);
|
||||
dst += len;
|
||||
src_addr = 0;
|
||||
words -= len / 2;
|
||||
continue;
|
||||
}
|
||||
memcpy(dst, cdc.ram + src_addr, words * 2);
|
||||
break;
|
||||
}
|
||||
goto update_dma;
|
||||
|
||||
case prg_ram_dma_w:
|
||||
dst_addr <<= 3;
|
||||
dst = Pico_mcd->prg_ram + dst_addr;
|
||||
dst_limit = 0x80000;
|
||||
break;
|
||||
|
||||
case word_ram_0_dma_w:
|
||||
dst_addr = (dst_addr << 3) & 0x1fffe;
|
||||
dst = Pico_mcd->word_ram1M[0] + dst_addr;
|
||||
dst_limit = 0x20000;
|
||||
break;
|
||||
|
||||
case word_ram_1_dma_w:
|
||||
dst_addr = (dst_addr << 3) & 0x1fffe;
|
||||
dst = Pico_mcd->word_ram1M[1] + dst_addr;
|
||||
dst_limit = 0x20000;
|
||||
break;
|
||||
|
||||
case word_ram_2M_dma_w:
|
||||
dst_addr = (dst_addr << 3) & 0x3fffe;
|
||||
dst = Pico_mcd->word_ram2M + dst_addr;
|
||||
dst_limit = 0x40000;
|
||||
break;
|
||||
|
||||
default:
|
||||
elprintf(EL_ANOMALY, "invalid dma: %d", type);
|
||||
goto update_dma;
|
||||
}
|
||||
|
||||
if (dst_addr + words * 2 > dst_limit) {
|
||||
elprintf(EL_ANOMALY, "cd dma %d oflow: %x %x", type, dst_addr, words);
|
||||
words = (dst_limit - dst_addr) / 2;
|
||||
}
|
||||
while (words > 0)
|
||||
{
|
||||
if (src_addr + words * 2 > 0x4000) {
|
||||
len = 0x4000 - src_addr;
|
||||
memcpy16bswap((void *)dst, cdc.ram + src_addr, len / 2);
|
||||
dst += len;
|
||||
src_addr = 0;
|
||||
words -= len / 2;
|
||||
continue;
|
||||
}
|
||||
memcpy16bswap((void *)dst, cdc.ram + src_addr, words);
|
||||
break;
|
||||
}
|
||||
|
||||
update_dma:
|
||||
/* update DMA addresses */
|
||||
cdc.dac += words_in * 2;
|
||||
if (type == pcm_ram_dma_w)
|
||||
dma_addr += words_in >> 1;
|
||||
else
|
||||
dma_addr += words_in >> 2;
|
||||
|
||||
Pico_mcd->s68k_regs[0x0a] = dma_addr >> 8;
|
||||
Pico_mcd->s68k_regs[0x0b] = dma_addr;
|
||||
}
|
||||
|
||||
void cdc_dma_update(void)
|
||||
{
|
||||
/* end of DMA transfer ? */
|
||||
//if (cdc.dbc < DMA_BYTES_PER_LINE)
|
||||
{
|
||||
/* transfer remaining words using 16-bit DMA */
|
||||
//cdc.dma_w((cdc.dbc + 1) >> 1);
|
||||
do_dma(cdc.dma_w, (cdc.dbc + 1) >> 1);
|
||||
|
||||
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
|
||||
cdc.dbc = 0xf000;
|
||||
|
||||
/* clear !DTEN and !DTBSY */
|
||||
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
|
||||
|
||||
/* pending Data Transfer End interrupt */
|
||||
cdc.ifstat &= ~BIT_DTEI;
|
||||
|
||||
/* Data Transfer End interrupt enabled ? */
|
||||
if (cdc.ifctrl & BIT_DTEIEN)
|
||||
{
|
||||
/* level 5 interrupt enabled ? */
|
||||
if (Pico_mcd->s68k_regs[0x32+1] & PCDS_IEN5)
|
||||
{
|
||||
/* update IRQ level */
|
||||
elprintf(EL_INTS, "cdc DTE irq 5");
|
||||
SekInterruptS68k(5);
|
||||
}
|
||||
}
|
||||
|
||||
/* clear DSR bit & set EDT bit (SCD register $04) */
|
||||
Pico_mcd->s68k_regs[0x04+0] = (Pico_mcd->s68k_regs[0x04+0] & 0x07) | 0x80;
|
||||
|
||||
/* disable DMA transfer */
|
||||
cdc.dma_w = 0;
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
{
|
||||
/* transfer all words using 16-bit DMA */
|
||||
cdc.dma_w(DMA_BYTES_PER_LINE >> 1);
|
||||
|
||||
/* decrement data byte counter */
|
||||
cdc.dbc -= length;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int cdc_decoder_update(uint8 header[4])
|
||||
{
|
||||
/* data decoding enabled ? */
|
||||
if (cdc.ctrl[0] & BIT_DECEN)
|
||||
{
|
||||
/* update HEAD registers */
|
||||
memcpy(cdc.head[0], header, sizeof(cdc.head[0]));
|
||||
|
||||
/* set !VALST */
|
||||
cdc.stat[3] = 0x00;
|
||||
|
||||
/* pending decoder interrupt */
|
||||
cdc.ifstat &= ~BIT_DECI;
|
||||
|
||||
/* decoder interrupt enabled ? */
|
||||
if (cdc.ifctrl & BIT_DECIEN)
|
||||
{
|
||||
/* level 5 interrupt enabled ? */
|
||||
if (Pico_mcd->s68k_regs[0x32+1] & PCDS_IEN5)
|
||||
{
|
||||
/* update IRQ level */
|
||||
elprintf(EL_INTS, "cdc DEC irq 5");
|
||||
SekInterruptS68k(5);
|
||||
}
|
||||
}
|
||||
|
||||
/* buffer RAM write enabled ? */
|
||||
if (cdc.ctrl[0] & BIT_WRRQ)
|
||||
{
|
||||
uint16 offset;
|
||||
|
||||
/* increment block pointer */
|
||||
cdc.pt += 2352;
|
||||
|
||||
/* increment write address */
|
||||
cdc.wa += 2352;
|
||||
|
||||
/* CDC buffer address */
|
||||
offset = cdc.pt & 0x3fff;
|
||||
|
||||
/* write CDD block header (4 bytes) */
|
||||
memcpy(cdc.ram + offset, header, 4);
|
||||
|
||||
/* write CDD block data (2048 bytes) */
|
||||
cdd_read_data(cdc.ram + 4 + offset);
|
||||
|
||||
/* take care of buffer overrun */
|
||||
if (offset > (0x4000 - 2048 - 4))
|
||||
{
|
||||
/* data should be written at the start of buffer */
|
||||
memcpy(cdc.ram, cdc.ram + 0x4000, offset + 2048 + 4 - 0x4000);
|
||||
}
|
||||
|
||||
/* read next data block */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* keep decoding same data block if Buffer Write is disabled */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cdc_reg_w(unsigned char data)
|
||||
{
|
||||
#ifdef LOG_CDC
|
||||
elprintf(EL_STATUS, "CDC register %X write 0x%04x", Pico_mcd->s68k_regs[0x04+1] & 0x0F, data);
|
||||
#endif
|
||||
switch (Pico_mcd->s68k_regs[0x04+1] & 0x0F)
|
||||
{
|
||||
case 0x01: /* IFCTRL */
|
||||
{
|
||||
/* pending interrupts ? */
|
||||
if (((data & BIT_DTEIEN) && !(cdc.ifstat & BIT_DTEI)) ||
|
||||
((data & BIT_DECIEN) && !(cdc.ifstat & BIT_DECI)))
|
||||
{
|
||||
/* level 5 interrupt enabled ? */
|
||||
if (Pico_mcd->s68k_regs[0x32+1] & PCDS_IEN5)
|
||||
{
|
||||
/* update IRQ level */
|
||||
elprintf(EL_INTS, "cdc pending irq 5");
|
||||
SekInterruptS68k(5);
|
||||
}
|
||||
}
|
||||
else // if (scd.pending & (1 << 5))
|
||||
{
|
||||
/* clear pending level 5 interrupts */
|
||||
SekInterruptClearS68k(5);
|
||||
}
|
||||
|
||||
/* abort any data transfer if data output is disabled */
|
||||
if (!(data & BIT_DOUTEN))
|
||||
{
|
||||
/* clear !DTBSY and !DTEN */
|
||||
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
|
||||
}
|
||||
|
||||
cdc.ifctrl = data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x02;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x02: /* DBCL */
|
||||
cdc.dbc &= 0xff00;
|
||||
cdc.dbc |= data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x03;
|
||||
break;
|
||||
|
||||
case 0x03: /* DBCH */
|
||||
cdc.dbc &= 0x00ff;
|
||||
cdc.dbc |= data << 8;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x04;
|
||||
break;
|
||||
|
||||
case 0x04: /* DACL */
|
||||
cdc.dac &= 0xff00;
|
||||
cdc.dac |= data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x05;
|
||||
break;
|
||||
|
||||
case 0x05: /* DACH */
|
||||
cdc.dac &= 0x00ff;
|
||||
cdc.dac |= data << 8;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x06;
|
||||
break;
|
||||
|
||||
case 0x06: /* DTRG */
|
||||
{
|
||||
/* start data transfer if data output is enabled */
|
||||
if (cdc.ifctrl & BIT_DOUTEN)
|
||||
{
|
||||
/* set !DTBSY */
|
||||
cdc.ifstat &= ~BIT_DTBSY;
|
||||
|
||||
/* clear DBCH bits 4-7 */
|
||||
cdc.dbc &= 0x0fff;
|
||||
|
||||
/* clear EDT & DSR bits (SCD register $04) */
|
||||
Pico_mcd->s68k_regs[0x04+0] &= 0x07;
|
||||
|
||||
cdc.dma_w = 0;
|
||||
|
||||
/* setup data transfer destination */
|
||||
switch (Pico_mcd->s68k_regs[0x04+0] & 0x07)
|
||||
{
|
||||
case 2: /* MAIN-CPU host read */
|
||||
case 3: /* SUB-CPU host read */
|
||||
{
|
||||
/* set !DTEN */
|
||||
cdc.ifstat &= ~BIT_DTEN;
|
||||
|
||||
/* set DSR bit (register $04) */
|
||||
Pico_mcd->s68k_regs[0x04+0] |= 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: /* PCM RAM DMA */
|
||||
{
|
||||
cdc.dma_w = pcm_ram_dma_w;
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: /* PRG-RAM DMA */
|
||||
{
|
||||
cdc.dma_w = prg_ram_dma_w;
|
||||
break;
|
||||
}
|
||||
|
||||
case 7: /* WORD-RAM DMA */
|
||||
{
|
||||
/* check memory mode */
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x04)
|
||||
{
|
||||
/* 1M mode */
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x01)
|
||||
{
|
||||
/* Word-RAM bank 0 is assigned to SUB-CPU */
|
||||
cdc.dma_w = word_ram_0_dma_w;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Word-RAM bank 1 is assigned to SUB-CPU */
|
||||
cdc.dma_w = word_ram_1_dma_w;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 2M mode */
|
||||
if (Pico_mcd->s68k_regs[0x02+1] & 0x02)
|
||||
{
|
||||
/* only process DMA if Word-RAM is assigned to SUB-CPU */
|
||||
cdc.dma_w = word_ram_2M_dma_w;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* invalid */
|
||||
{
|
||||
elprintf(EL_ANOMALY, "invalid CDC tranfer destination (%d)",
|
||||
Pico_mcd->s68k_regs[0x04+0] & 0x07);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cdc.dma_w)
|
||||
pcd_event_schedule_s68k(PCD_EVENT_DMA, cdc.dbc / 2);
|
||||
}
|
||||
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x07;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x07: /* DTACK */
|
||||
{
|
||||
/* clear pending data transfer end interrupt */
|
||||
cdc.ifstat |= BIT_DTEI;
|
||||
|
||||
/* clear DBCH bits 4-7 */
|
||||
cdc.dbc &= 0x0fff;
|
||||
|
||||
#if 0
|
||||
/* no pending decoder interrupt ? */
|
||||
if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
|
||||
{
|
||||
/* clear pending level 5 interrupt */
|
||||
SekInterruptClearS68k(5);
|
||||
}
|
||||
#endif
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x08;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x08: /* WAL */
|
||||
cdc.wa &= 0xff00;
|
||||
cdc.wa |= data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x09;
|
||||
break;
|
||||
|
||||
case 0x09: /* WAH */
|
||||
cdc.wa &= 0x00ff;
|
||||
cdc.wa |= data << 8;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0a;
|
||||
break;
|
||||
|
||||
case 0x0a: /* CTRL0 */
|
||||
{
|
||||
/* set CRCOK bit only if decoding is enabled */
|
||||
cdc.stat[0] = data & BIT_DECEN;
|
||||
|
||||
/* update decoding mode */
|
||||
if (data & BIT_AUTORQ)
|
||||
{
|
||||
/* set MODE bit according to CTRL1 register & clear FORM bit */
|
||||
cdc.stat[2] = cdc.ctrl[1] & BIT_MODRQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set MODE & FORM bits according to CTRL1 register */
|
||||
cdc.stat[2] = cdc.ctrl[1] & (BIT_MODRQ | BIT_FORMRQ);
|
||||
}
|
||||
|
||||
cdc.ctrl[0] = data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0b;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0b: /* CTRL1 */
|
||||
{
|
||||
/* update decoding mode */
|
||||
if (cdc.ctrl[0] & BIT_AUTORQ)
|
||||
{
|
||||
/* set MODE bit according to CTRL1 register & clear FORM bit */
|
||||
cdc.stat[2] = data & BIT_MODRQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set MODE & FORM bits according to CTRL1 register */
|
||||
cdc.stat[2] = data & (BIT_MODRQ | BIT_FORMRQ);
|
||||
}
|
||||
|
||||
cdc.ctrl[1] = data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0c;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0c: /* PTL */
|
||||
cdc.pt &= 0xff00;
|
||||
cdc.pt |= data;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0d;
|
||||
break;
|
||||
|
||||
case 0x0d: /* PTH */
|
||||
cdc.pt &= 0x00ff;
|
||||
cdc.pt |= data << 8;
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0e;
|
||||
break;
|
||||
|
||||
case 0x0e: /* CTRL2 (unused) */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0f: /* RESET */
|
||||
cdc_reset();
|
||||
break;
|
||||
|
||||
default: /* by default, SBOUT is not used */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char cdc_reg_r(void)
|
||||
{
|
||||
switch (Pico_mcd->s68k_regs[0x04+1] & 0x0F)
|
||||
{
|
||||
case 0x01: /* IFSTAT */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x02;
|
||||
return cdc.ifstat;
|
||||
|
||||
case 0x02: /* DBCL */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x03;
|
||||
return cdc.dbc & 0xff;
|
||||
|
||||
case 0x03: /* DBCH */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x04;
|
||||
return (cdc.dbc >> 8) & 0xff;
|
||||
|
||||
case 0x04: /* HEAD0 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x05;
|
||||
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][0];
|
||||
|
||||
case 0x05: /* HEAD1 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x06;
|
||||
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][1];
|
||||
|
||||
case 0x06: /* HEAD2 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x07;
|
||||
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][2];
|
||||
|
||||
case 0x07: /* HEAD3 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x08;
|
||||
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][3];
|
||||
|
||||
case 0x08: /* PTL */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x09;
|
||||
return cdc.pt & 0xff;
|
||||
|
||||
case 0x09: /* PTH */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0a;
|
||||
return (cdc.pt >> 8) & 0xff;
|
||||
|
||||
case 0x0a: /* WAL */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0b;
|
||||
return cdc.wa & 0xff;
|
||||
|
||||
case 0x0b: /* WAH */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0c;
|
||||
return (cdc.wa >> 8) & 0xff;
|
||||
|
||||
case 0x0c: /* STAT0 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0d;
|
||||
return cdc.stat[0];
|
||||
|
||||
case 0x0d: /* STAT1 (always return 0) */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0e;
|
||||
return 0x00;
|
||||
|
||||
case 0x0e: /* STAT2 */
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x0f;
|
||||
return cdc.stat[2];
|
||||
|
||||
case 0x0f: /* STAT3 */
|
||||
{
|
||||
uint8 data = cdc.stat[3];
|
||||
|
||||
/* clear !VALST (note: this is not 100% correct but BIOS do not seem to care) */
|
||||
cdc.stat[3] = BIT_VALST;
|
||||
|
||||
/* clear pending decoder interrupt */
|
||||
cdc.ifstat |= BIT_DECI;
|
||||
|
||||
#if 0
|
||||
/* no pending data transfer end interrupt */
|
||||
if ((cdc.ifstat | BIT_DTEI) || !(cdc.ifctrl & BIT_DTEIEN))
|
||||
{
|
||||
/* clear pending level 5 interrupt */
|
||||
SekInterruptClearS68k(5);
|
||||
}
|
||||
#endif
|
||||
|
||||
Pico_mcd->s68k_regs[0x04+1] = 0x00;
|
||||
return data;
|
||||
}
|
||||
|
||||
default: /* by default, COMIN is always empty */
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short cdc_host_r(void)
|
||||
{
|
||||
/* check if data is available */
|
||||
if (!(cdc.ifstat & BIT_DTEN))
|
||||
{
|
||||
/* read data word from CDC RAM buffer */
|
||||
uint8 *datap = cdc.ram + (cdc.dac & 0x3ffe);
|
||||
uint16 data = (datap[0] << 8) | datap[1];
|
||||
|
||||
#ifdef LOG_CDC
|
||||
error("CDC host read 0x%04x -> 0x%04x (dbc=0x%x) (%X)\n", cdc.dac, data, cdc.dbc, s68k.pc);
|
||||
#endif
|
||||
|
||||
/* increment data address counter */
|
||||
cdc.dac += 2;
|
||||
|
||||
/* decrement data byte counter */
|
||||
cdc.dbc -= 2;
|
||||
|
||||
/* end of transfer ? */
|
||||
if ((int16)cdc.dbc <= 0)
|
||||
{
|
||||
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
|
||||
cdc.dbc = 0xf000;
|
||||
|
||||
/* clear !DTEN and !DTBSY */
|
||||
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
|
||||
|
||||
/* pending Data Transfer End interrupt */
|
||||
cdc.ifstat &= ~BIT_DTEI;
|
||||
|
||||
/* Data Transfer End interrupt enabled ? */
|
||||
if (cdc.ifctrl & BIT_DTEIEN)
|
||||
{
|
||||
/* level 5 interrupt enabled ? */
|
||||
if (Pico_mcd->s68k_regs[0x32+1] & PCDS_IEN5)
|
||||
{
|
||||
/* update IRQ level */
|
||||
elprintf(EL_INTS, "cdc DTE irq 5");
|
||||
SekInterruptS68k(5);
|
||||
}
|
||||
}
|
||||
|
||||
/* clear DSR bit & set EDT bit (SCD register $04) */
|
||||
Pico_mcd->s68k_regs[0x04+0] = (Pico_mcd->s68k_regs[0x04+0] & 0x07) | 0x80;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#ifdef LOG_CDC
|
||||
error("error reading CDC host (data transfer disabled)\n");
|
||||
#endif
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
|||
/***************************************************************************************
|
||||
* Genesis Plus
|
||||
* CD drive processor & CD-DA fader
|
||||
*
|
||||
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX)
|
||||
*
|
||||
* Redistribution and use of this code or any derivative works are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions may not be sold, nor may they be used in a commercial
|
||||
* product or activity.
|
||||
*
|
||||
* - Redistributions that are modified from the original source must include the
|
||||
* complete source code, including the source code for all components used by a
|
||||
* binary built from the modified sources. However, as a special exception, the
|
||||
* source code distributed need not include anything that is normally distributed
|
||||
* (in either source or binary form) with the major components (compiler, kernel,
|
||||
* and so on) of the operating system on which the executable runs, unless that
|
||||
* component itself accompanies the executable.
|
||||
*
|
||||
* - Redistributions must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************************/
|
||||
#ifndef _HW_CDD_
|
||||
#define _HW_CDD_
|
||||
|
||||
#ifdef USE_LIBTREMOR
|
||||
#include "tremor/ivorbisfile.h"
|
||||
#endif
|
||||
|
||||
/* CDD status */
|
||||
#define NO_DISC 0x00
|
||||
#define CD_PLAY 0x01
|
||||
#define CD_SEEK 0x02
|
||||
#define CD_SCAN 0x03
|
||||
#define CD_READY 0x04
|
||||
#define CD_OPEN 0x05 /* similar to 0x0E ? */
|
||||
#define CD_STOP 0x09
|
||||
#define CD_END 0x0C
|
||||
|
||||
/* CD blocks scanning speed */
|
||||
#define CD_SCAN_SPEED 30
|
||||
|
||||
#define CD_MAX_TRACKS 100
|
||||
|
||||
/* CD track */
|
||||
typedef struct
|
||||
{
|
||||
void *fd;
|
||||
#ifdef USE_LIBTREMOR
|
||||
OggVorbis_File vf;
|
||||
#endif
|
||||
int offset;
|
||||
int start;
|
||||
int end;
|
||||
} track_t;
|
||||
|
||||
/* CD TOC */
|
||||
typedef struct
|
||||
{
|
||||
int end;
|
||||
int last;
|
||||
track_t tracks[CD_MAX_TRACKS];
|
||||
} toc_t;
|
||||
|
||||
/* CDD hardware */
|
||||
typedef struct
|
||||
{
|
||||
uint32 cycles;
|
||||
uint32 latency;
|
||||
int loaded;
|
||||
int index;
|
||||
int lba;
|
||||
int scanOffset;
|
||||
int volume;
|
||||
uint8 status;
|
||||
uint16 sectorSize;
|
||||
toc_t toc;
|
||||
int16 audio[2];
|
||||
} cdd_t;
|
||||
|
||||
extern cdd_t cdd;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Convert "cell arrange" address to normal address.
|
||||
* (C) notaz, 2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
// 64 x32 x16 x8 x4 x4
|
||||
static unsigned int cell_map(int celln)
|
||||
{
|
||||
int col, row;
|
||||
|
||||
switch ((celln >> 12) & 7) { // 0-0x8000
|
||||
case 0: // x32 cells
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
col = celln >> 8;
|
||||
row = celln & 0xff;
|
||||
break;
|
||||
case 4: // x16
|
||||
case 5:
|
||||
col = celln >> 7;
|
||||
row = celln & 0x7f;
|
||||
row |= 0x10000 >> 8;
|
||||
break;
|
||||
case 6: // x8
|
||||
col = celln >> 6;
|
||||
row = celln & 0x3f;
|
||||
row |= 0x18000 >> 8;
|
||||
break;
|
||||
case 7: // x4
|
||||
col = celln >> 5;
|
||||
row = celln & 0x1f;
|
||||
row |= (celln & 0x7800) >> 6;
|
||||
break;
|
||||
default: // never happens, only here to make compiler happy
|
||||
col = row = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return (col & 0x3f) + row*64;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* cuefile handling
|
||||
* (C) notaz, 2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cue.h"
|
||||
|
||||
#include "../pico_int.h"
|
||||
// #define elprintf(w,f,...) printf(f "\n",##__VA_ARGS__);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#ifdef __EPOC32__
|
||||
#define snprintf(b,s,...) sprintf(b,##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define BEGINS(buff,str) (strncmp(buff,str,sizeof(str)-1) == 0)
|
||||
|
||||
void cue_destroy(cue_data_t *data)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (data == NULL) return;
|
||||
|
||||
for (c = data->track_count; c > 0; c--)
|
||||
if (data->tracks[c].fname != NULL)
|
||||
free(data->tracks[c].fname);
|
||||
free(data);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
typedef enum
|
||||
{
|
||||
CT_UNKNOWN = 0,
|
||||
CT_ISO = 1, /* 2048 B/sector */
|
||||
CT_BIN = 2, /* 2352 B/sector */
|
||||
CT_MP3 = 3,
|
||||
CT_WAV = 4
|
||||
} cue_track_type;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *fname;
|
||||
int pregap; /* pregap for current track */
|
||||
int sector_offset; /* in current file */
|
||||
int sector_xlength;
|
||||
cue_track_type type;
|
||||
} cue_track;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int track_count;
|
||||
cue_track tracks[0];
|
||||
} cue_data_t;
|
||||
|
||||
|
||||
cue_data_t *cue_parse(const char *fname);
|
||||
void cue_destroy(cue_data_t *data);
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#undef uint8
|
||||
#undef uint16
|
||||
#undef uint32
|
||||
#undef int8
|
||||
#undef int16
|
||||
#undef int32
|
||||
|
||||
#define uint8 unsigned char
|
||||
#define uint16 unsigned short
|
||||
#define uint32 unsigned int
|
||||
#define int8 signed char
|
||||
#define int16 signed short
|
||||
#define int32 signed int
|
||||
|
||||
#define READ_BYTE(BASE, ADDR) (BASE)[(ADDR)^1]
|
||||
#define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[(ADDR)^1] = (VAL)
|
||||
|
||||
#define load_param(param, size) \
|
||||
memcpy(param, &state[bufferptr], size); \
|
||||
bufferptr += size;
|
||||
|
||||
#define save_param(param, size) \
|
||||
memcpy(&state[bufferptr], param, size); \
|
||||
bufferptr += size;
|
|
@ -0,0 +1,451 @@
|
|||
/***************************************************************************************
|
||||
* Genesis Plus
|
||||
* CD graphics processor
|
||||
*
|
||||
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
|
||||
*
|
||||
* Redistribution and use of this code or any derivative works are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions may not be sold, nor may they be used in a commercial
|
||||
* product or activity.
|
||||
*
|
||||
* - Redistributions that are modified from the original source must include the
|
||||
* complete source code, including the source code for all components used by a
|
||||
* binary built from the modified sources. However, as a special exception, the
|
||||
* source code distributed need not include anything that is normally distributed
|
||||
* (in either source or binary form) with the major components (compiler, kernel,
|
||||
* and so on) of the operating system on which the executable runs, unless that
|
||||
* component itself accompanies the executable.
|
||||
*
|
||||
* - Redistributions must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************************/
|
||||
#include "../pico_int.h"
|
||||
#include "genplus_macros.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//uint32 cycles; /* current cycles count for graphics operation */
|
||||
//uint32 cyclesPerLine; /* current graphics operation timings */
|
||||
uint32 dotMask; /* stamp map size mask */
|
||||
uint16 *tracePtr; /* trace vector pointer */
|
||||
uint16 *mapPtr; /* stamp map table base address */
|
||||
uint8 stampShift; /* stamp pixel shift value (related to stamp size) */
|
||||
uint8 mapShift; /* stamp map table shift value (related to stamp map size) */
|
||||
uint16 bufferOffset; /* image buffer column offset */
|
||||
uint32 bufferStart; /* image buffer start index */
|
||||
uint32 y_step; /* pico: render line step */
|
||||
uint8 lut_prio[4][0x10][0x10]; /* WORD-RAM data writes priority lookup table */
|
||||
uint8 lut_pixel[0x200]; /* Graphics operation dot offset lookup table */
|
||||
uint8 lut_cell[0x100]; /* Graphics operation stamp offset lookup table */
|
||||
} gfx_t;
|
||||
|
||||
static gfx_t gfx;
|
||||
|
||||
static void gfx_schedule(void);
|
||||
|
||||
/***************************************************************/
|
||||
/* Rotation / Scaling operation (2M Mode) */
|
||||
/***************************************************************/
|
||||
|
||||
void gfx_init(void)
|
||||
{
|
||||
int i, j;
|
||||
uint8 mask, row, col, temp;
|
||||
|
||||
memset(&gfx, 0, sizeof(gfx));
|
||||
|
||||
/* Initialize priority modes lookup table */
|
||||
for (i = 0; i < 0x10; i++)
|
||||
{
|
||||
for (j = 0; j < 0x10; j++)
|
||||
{
|
||||
/* normal */
|
||||
gfx.lut_prio[0][i][j] = j;
|
||||
/* underwrite */
|
||||
gfx.lut_prio[1][i][j] = i ? i : j;
|
||||
/* overwrite */
|
||||
gfx.lut_prio[2][i][j] = j ? j : i;
|
||||
/* invalid */
|
||||
gfx.lut_prio[3][i][j] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize cell lookup table */
|
||||
/* table entry = yyxxshrr (8 bits) */
|
||||
/* with: yy = cell row (0-3) */
|
||||
/* xx = cell column (0-3) */
|
||||
/* s = stamp size (0=16x16, 1=32x32) */
|
||||
/* hrr = HFLIP & ROTATION bits */
|
||||
for (i=0; i<0x100; i++)
|
||||
{
|
||||
/* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */
|
||||
mask = (i & 8) ? 3 : 1;
|
||||
row = (i >> 6) & mask;
|
||||
col = (i >> 4) & mask;
|
||||
|
||||
if (i & 4) { col = col ^ mask; } /* HFLIP (always first) */
|
||||
if (i & 2) { col = col ^ mask; row = row ^ mask; } /* ROLL1 */
|
||||
if (i & 1) { temp = col; col = row ^ mask; row = temp; } /* ROLL0 */
|
||||
|
||||
/* cell offset (0-3 or 0-15) */
|
||||
gfx.lut_cell[i] = row + col * (mask + 1);
|
||||
}
|
||||
|
||||
/* Initialize pixel lookup table */
|
||||
/* table entry = yyyxxxhrr (9 bits) */
|
||||
/* with: yyy = pixel row (0-7) */
|
||||
/* xxx = pixel column (0-7) */
|
||||
/* hrr = HFLIP & ROTATION bits */
|
||||
for (i=0; i<0x200; i++)
|
||||
{
|
||||
/* one cell = 8x8 pixels */
|
||||
row = (i >> 6) & 7;
|
||||
col = (i >> 3) & 7;
|
||||
|
||||
if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */
|
||||
if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */
|
||||
if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */
|
||||
|
||||
/* pixel offset (0-63) */
|
||||
gfx.lut_pixel[i] = col + row * 8;
|
||||
}
|
||||
}
|
||||
|
||||
int gfx_context_save(uint8 *state)
|
||||
{
|
||||
uint32 tmp32;
|
||||
int bufferptr = 0;
|
||||
|
||||
//save_param(&gfx.cycles, sizeof(gfx.cycles));
|
||||
//save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
|
||||
save_param(&gfx.dotMask, sizeof(gfx.dotMask));
|
||||
save_param(&gfx.stampShift, sizeof(gfx.stampShift));
|
||||
save_param(&gfx.mapShift, sizeof(gfx.mapShift));
|
||||
save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
|
||||
save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
|
||||
|
||||
tmp32 = (uint8 *)(gfx.tracePtr) - Pico_mcd->word_ram2M;
|
||||
save_param(&tmp32, 4);
|
||||
|
||||
tmp32 = (uint8 *)(gfx.mapPtr) - Pico_mcd->word_ram2M;
|
||||
save_param(&tmp32, 4);
|
||||
|
||||
save_param(&gfx.y_step, sizeof(gfx.y_step));
|
||||
|
||||
return bufferptr;
|
||||
}
|
||||
|
||||
int gfx_context_load(const uint8 *state)
|
||||
{
|
||||
uint32 tmp32;
|
||||
int bufferptr = 0;
|
||||
|
||||
//load_param(&gfx.cycles, sizeof(gfx.cycles));
|
||||
//load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
|
||||
load_param(&gfx.dotMask, sizeof(gfx.dotMask));
|
||||
load_param(&gfx.stampShift, sizeof(gfx.stampShift));
|
||||
load_param(&gfx.mapShift, sizeof(gfx.mapShift));
|
||||
load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
|
||||
load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
|
||||
|
||||
load_param(&tmp32, 4);
|
||||
gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
|
||||
|
||||
load_param(&tmp32, 4);
|
||||
gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
|
||||
|
||||
load_param(&gfx.y_step, sizeof(gfx.y_step));
|
||||
|
||||
return bufferptr;
|
||||
}
|
||||
|
||||
static void gfx_render(uint32 bufferIndex, uint32 width)
|
||||
{
|
||||
uint8 pixel_in, pixel_out;
|
||||
uint16 stamp_data;
|
||||
uint32 stamp_index;
|
||||
uint32 priority;
|
||||
|
||||
/* pixel map start position for current line (13.3 format converted to 13.11) */
|
||||
uint32 xpos = *gfx.tracePtr++ << 8;
|
||||
uint32 ypos = *gfx.tracePtr++ << 8;
|
||||
|
||||
/* pixel map offset values for current line (5.11 format) */
|
||||
uint32 xoffset = (int16) *gfx.tracePtr++;
|
||||
uint32 yoffset = (int16) *gfx.tracePtr++;
|
||||
|
||||
priority = (Pico_mcd->s68k_regs[2] << 8) | Pico_mcd->s68k_regs[3];
|
||||
priority = (priority >> 3) & 0x03;
|
||||
|
||||
/* process all dots */
|
||||
while (width--)
|
||||
{
|
||||
/* check if stamp map is repeated */
|
||||
if (Pico_mcd->s68k_regs[0x58+1] & 0x01)
|
||||
{
|
||||
/* stamp map range */
|
||||
xpos &= gfx.dotMask;
|
||||
ypos &= gfx.dotMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 24-bit range */
|
||||
xpos &= 0xffffff;
|
||||
ypos &= 0xffffff;
|
||||
}
|
||||
|
||||
/* check if pixel is outside stamp map */
|
||||
if ((xpos | ypos) & ~gfx.dotMask)
|
||||
{
|
||||
/* force pixel output to 0 */
|
||||
pixel_out = 0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* read stamp map table data */
|
||||
stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
|
||||
|
||||
/* stamp generator base index */
|
||||
/* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
|
||||
/* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */
|
||||
/* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */
|
||||
/* yyy = line offset (0-7) */
|
||||
/* xxx = pixel offset (0-7) */
|
||||
stamp_index = (stamp_data & 0x7ff) << 8;
|
||||
|
||||
if (stamp_index)
|
||||
{
|
||||
/* extract HFLIP & ROTATION bits */
|
||||
stamp_data = (stamp_data >> 13) & 7;
|
||||
|
||||
/* cell offset (0-3 or 0-15) */
|
||||
/* table entry = yyxxshrr (8 bits) */
|
||||
/* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */
|
||||
/* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
|
||||
/* s = stamp size (0=16x16, 1=32x32) */
|
||||
/* hrr = HFLIP & ROTATION bits */
|
||||
stamp_index |= gfx.lut_cell[
|
||||
stamp_data | ((Pico_mcd->s68k_regs[0x58+1] & 0x02) << 2 )
|
||||
| ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;
|
||||
|
||||
/* pixel offset (0-63) */
|
||||
/* table entry = yyyxxxhrr (9 bits) */
|
||||
/* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */
|
||||
/* xxx = pixel column (0-7) = (xpos >> 11) & 7 */
|
||||
/* hrr = HFLIP & ROTATION bits */
|
||||
stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];
|
||||
|
||||
/* read pixel pair (2 pixels/byte) */
|
||||
pixel_out = READ_BYTE(Pico_mcd->word_ram2M, stamp_index >> 1);
|
||||
|
||||
/* extract left or rigth pixel */
|
||||
if (stamp_index & 1)
|
||||
{
|
||||
pixel_out &= 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_out >>= 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* stamp 0 is not used: force pixel output to 0 */
|
||||
pixel_out = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
/* read out paired pixel data */
|
||||
pixel_in = READ_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1);
|
||||
|
||||
/* update left or rigth pixel */
|
||||
if (bufferIndex & 1)
|
||||
{
|
||||
/* priority mode write */
|
||||
pixel_out = gfx.lut_prio[priority][pixel_in & 0x0f][pixel_out];
|
||||
|
||||
pixel_out |= (pixel_in & 0xf0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* priority mode write */
|
||||
pixel_out = gfx.lut_prio[priority][pixel_in >> 4][pixel_out];
|
||||
|
||||
pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);
|
||||
}
|
||||
|
||||
/* write data to image buffer */
|
||||
WRITE_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1, pixel_out);
|
||||
|
||||
/* check current pixel position */
|
||||
if ((bufferIndex & 7) != 7)
|
||||
{
|
||||
/* next pixel */
|
||||
bufferIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* next cell: increment image buffer offset by one column (minus 7 pixels) */
|
||||
bufferIndex += gfx.bufferOffset;
|
||||
}
|
||||
|
||||
/* increment pixel position */
|
||||
xpos += xoffset;
|
||||
ypos += yoffset;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_start(unsigned int base)
|
||||
{
|
||||
/* make sure 2M mode is enabled */
|
||||
if (!(Pico_mcd->s68k_regs[3] & 0x04))
|
||||
{
|
||||
uint32 mask = 0;
|
||||
uint32 reg;
|
||||
|
||||
/* trace vector pointer */
|
||||
gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + ((base << 2) & 0x3fff8));
|
||||
|
||||
/* stamps & stamp map size */
|
||||
switch ((Pico_mcd->s68k_regs[0x58+1] >> 1) & 0x03)
|
||||
{
|
||||
case 0:
|
||||
gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
|
||||
gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
|
||||
gfx.mapShift = 4; /* 16x16 stamps/map */
|
||||
mask = 0x3fe00; /* 512 bytes/table */
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
|
||||
gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
|
||||
gfx.mapShift = 3; /* 8x8 stamps/map */
|
||||
mask = 0x3ff80; /* 128 bytes/table */
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
|
||||
gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
|
||||
gfx.mapShift = 8; /* 256x256 stamps/map */
|
||||
mask = 0x20000; /* 131072 bytes/table */
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
|
||||
gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
|
||||
gfx.mapShift = 7; /* 128x128 stamps/map */
|
||||
mask = 0x38000; /* 32768 bytes/table */
|
||||
break;
|
||||
}
|
||||
|
||||
/* stamp map table base address */
|
||||
reg = (Pico_mcd->s68k_regs[0x5a] << 8) | Pico_mcd->s68k_regs[0x5b];
|
||||
gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + ((reg << 2) & mask));
|
||||
|
||||
/* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
|
||||
gfx.bufferOffset = (((Pico_mcd->s68k_regs[0x5c+1] & 0x1f) + 1) << 6) - 7;
|
||||
|
||||
/* image buffer start index in dot units (2 pixels/byte) */
|
||||
reg = (Pico_mcd->s68k_regs[0x5e] << 8) | Pico_mcd->s68k_regs[0x5f];
|
||||
gfx.bufferStart = (reg << 3) & 0x7ffc0;
|
||||
|
||||
/* add image buffer horizontal dot offset */
|
||||
gfx.bufferStart += (Pico_mcd->s68k_regs[0x60+1] & 0x3f);
|
||||
|
||||
/* reset GFX chip cycle counter */
|
||||
//gfx.cycles = cycles;
|
||||
|
||||
/* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
|
||||
//gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
|
||||
|
||||
/* start graphics operation */
|
||||
Pico_mcd->s68k_regs[0x58] = 0x80;
|
||||
|
||||
gfx_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
/* PicoDrive specific */
|
||||
#define UPDATE_CYCLES 20000
|
||||
|
||||
static void gfx_schedule(void)
|
||||
{
|
||||
int w, h, cycles;
|
||||
int y_step;
|
||||
|
||||
w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
|
||||
h = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
|
||||
|
||||
cycles = 5 * w * h;
|
||||
if (cycles > UPDATE_CYCLES)
|
||||
y_step = (UPDATE_CYCLES + 5 * w - 1) / (5 * w);
|
||||
else
|
||||
y_step = h;
|
||||
|
||||
gfx.y_step = y_step;
|
||||
pcd_event_schedule_s68k(PCD_EVENT_GFX, 5 * w * y_step);
|
||||
}
|
||||
|
||||
void gfx_update(unsigned int cycles)
|
||||
{
|
||||
int lines, lines_reg;
|
||||
int w;
|
||||
|
||||
if (!(Pico_mcd->s68k_regs[0x58] & 0x80))
|
||||
return;
|
||||
|
||||
w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
|
||||
lines = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
|
||||
lines_reg = lines - gfx.y_step;
|
||||
|
||||
if (lines_reg <= 0) {
|
||||
Pico_mcd->s68k_regs[0x58] = 0;
|
||||
Pico_mcd->s68k_regs[0x64] =
|
||||
Pico_mcd->s68k_regs[0x65] = 0;
|
||||
|
||||
if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
|
||||
elprintf(EL_INTS|EL_CD, "s68k: gfx_cd irq 1");
|
||||
SekInterruptS68k(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Pico_mcd->s68k_regs[0x64] = lines_reg >> 8;
|
||||
Pico_mcd->s68k_regs[0x65] = lines_reg;
|
||||
|
||||
if (lines > gfx.y_step)
|
||||
lines = gfx.y_step;
|
||||
|
||||
pcd_event_schedule(cycles, PCD_EVENT_GFX, 5 * w * lines);
|
||||
}
|
||||
|
||||
if (PicoOpt & POPT_EN_MCD_GFX)
|
||||
{
|
||||
/* render lines */
|
||||
while (lines--)
|
||||
{
|
||||
/* process dots to image buffer */
|
||||
gfx_render(gfx.bufferStart, w);
|
||||
|
||||
/* increment image buffer start index for next line (8 pixels/line) */
|
||||
gfx.bufferStart += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2007
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
|
||||
#include "cell_map.c"
|
||||
|
||||
#ifndef UTYPES_DEFINED
|
||||
typedef unsigned short u16;
|
||||
#endif
|
||||
|
||||
// check: Heart of the alien, jaguar xj 220
|
||||
PICO_INTERNAL void DmaSlowCell(unsigned int source, unsigned int a, int len, unsigned char inc)
|
||||
{
|
||||
unsigned char *base;
|
||||
unsigned int asrc, a2;
|
||||
u16 *r;
|
||||
|
||||
base = Pico_mcd->word_ram1M[Pico_mcd->s68k_regs[3]&1];
|
||||
|
||||
switch (Pico.video.type)
|
||||
{
|
||||
case 1: // vram
|
||||
r = Pico.vram;
|
||||
for(; len; len--)
|
||||
{
|
||||
asrc = cell_map(source >> 2) << 2;
|
||||
asrc |= source & 2;
|
||||
// if(a&1) d=(d<<8)|(d>>8); // ??
|
||||
r[a>>1] = *(u16 *)(base + asrc);
|
||||
source += 2;
|
||||
// AutoIncrement
|
||||
a=(u16)(a+inc);
|
||||
}
|
||||
rendstatus |= PDRAW_SPRITES_MOVED;
|
||||
break;
|
||||
|
||||
case 3: // cram
|
||||
Pico.m.dirtyPal = 1;
|
||||
r = Pico.cram;
|
||||
for(a2=a&0x7f; len; len--)
|
||||
{
|
||||
asrc = cell_map(source >> 2) << 2;
|
||||
asrc |= source & 2;
|
||||
r[a2>>1] = *(u16 *)(base + asrc);
|
||||
source += 2;
|
||||
// AutoIncrement
|
||||
a2+=inc;
|
||||
// good dest?
|
||||
if(a2 >= 0x80) break;
|
||||
}
|
||||
a=(a&0xff00)|a2;
|
||||
break;
|
||||
|
||||
case 5: // vsram[a&0x003f]=d;
|
||||
r = Pico.vsram;
|
||||
for(a2=a&0x7f; len; len--)
|
||||
{
|
||||
asrc = cell_map(source >> 2) << 2;
|
||||
asrc |= source & 2;
|
||||
r[a2>>1] = *(u16 *)(base + asrc);
|
||||
source += 2;
|
||||
// AutoIncrement
|
||||
a2+=inc;
|
||||
// good dest?
|
||||
if(a2 >= 0x80) break;
|
||||
}
|
||||
a=(a&0xff00)|a2;
|
||||
break;
|
||||
}
|
||||
// remember addr
|
||||
Pico.video.addr=(u16)a;
|
||||
}
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2007,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
#include "../sound/ym2612.h"
|
||||
|
||||
extern unsigned char formatted_bram[4*0x10];
|
||||
|
||||
static unsigned int mcd_m68k_cycle_mult;
|
||||
static unsigned int mcd_m68k_cycle_base;
|
||||
static unsigned int mcd_s68k_cycle_base;
|
||||
|
||||
void (*PicoMCDopenTray)(void) = NULL;
|
||||
void (*PicoMCDcloseTray)(void) = NULL;
|
||||
|
||||
|
||||
PICO_INTERNAL void PicoInitMCD(void)
|
||||
{
|
||||
SekInitS68k();
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoExitMCD(void)
|
||||
{
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPowerMCD(void)
|
||||
{
|
||||
SekCycleCntS68k = SekCycleAimS68k = 0;
|
||||
|
||||
int fmt_size = sizeof(formatted_bram);
|
||||
memset(Pico_mcd->prg_ram, 0, sizeof(Pico_mcd->prg_ram));
|
||||
memset(Pico_mcd->word_ram2M, 0, sizeof(Pico_mcd->word_ram2M));
|
||||
memset(Pico_mcd->pcm_ram, 0, sizeof(Pico_mcd->pcm_ram));
|
||||
memset(Pico_mcd->bram, 0, sizeof(Pico_mcd->bram));
|
||||
memcpy(Pico_mcd->bram + sizeof(Pico_mcd->bram) - fmt_size,
|
||||
formatted_bram, fmt_size);
|
||||
memset(Pico_mcd->s68k_regs, 0, sizeof(Pico_mcd->s68k_regs));
|
||||
memset(&Pico_mcd->pcm, 0, sizeof(Pico_mcd->pcm));
|
||||
memset(&Pico_mcd->m, 0, sizeof(Pico_mcd->m));
|
||||
|
||||
cdc_init();
|
||||
gfx_init();
|
||||
|
||||
// cold reset state (tested)
|
||||
Pico_mcd->m.state_flags = PCD_ST_S68K_RST;
|
||||
Pico_mcd->m.busreq = 2; // busreq on, s68k in reset
|
||||
Pico_mcd->s68k_regs[3] = 1; // 2M word RAM mode, m68k access
|
||||
memset(Pico_mcd->bios + 0x70, 0xff, 4);
|
||||
}
|
||||
|
||||
void pcd_soft_reset(void)
|
||||
{
|
||||
elprintf(EL_CD, "cd: soft reset");
|
||||
|
||||
Pico_mcd->m.s68k_pend_ints = 0;
|
||||
cdc_reset();
|
||||
cdd_reset();
|
||||
#ifdef _ASM_CD_MEMORY_C
|
||||
//PicoMemResetCDdecode(1); // don't have to call this in 2M mode
|
||||
#endif
|
||||
|
||||
memset(&Pico_mcd->s68k_regs[0x38], 0, 9);
|
||||
Pico_mcd->s68k_regs[0x38+9] = 0x0f; // default checksum
|
||||
|
||||
pcd_event_schedule_s68k(PCD_EVENT_CDC, 12500000/75);
|
||||
|
||||
// TODO: test if register state/timers change
|
||||
}
|
||||
|
||||
PICO_INTERNAL int PicoResetMCD(void)
|
||||
{
|
||||
// reset button doesn't affect MCD hardware
|
||||
|
||||
// use SRam.data for RAM cart
|
||||
if (PicoOpt & POPT_EN_MCD_RAMCART) {
|
||||
if (SRam.data == NULL)
|
||||
SRam.data = calloc(1, 0x12000);
|
||||
}
|
||||
else if (SRam.data != NULL) {
|
||||
free(SRam.data);
|
||||
SRam.data = NULL;
|
||||
}
|
||||
SRam.start = SRam.end = 0; // unused
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SekRunM68kOnce(void)
|
||||
{
|
||||
int cyc_do;
|
||||
pevt_log_m68k_o(EVT_RUN_START);
|
||||
|
||||
if ((cyc_do = SekCycleAim - SekCycleCnt) > 0) {
|
||||
SekCycleCnt += cyc_do;
|
||||
|
||||
#if defined(EMU_C68K)
|
||||
PicoCpuCM68k.cycles = cyc_do;
|
||||
CycloneRun(&PicoCpuCM68k);
|
||||
SekCycleCnt -= PicoCpuCM68k.cycles;
|
||||
#elif defined(EMU_M68K)
|
||||
SekCycleCnt += m68k_execute(cyc_do) - cyc_do;
|
||||
#elif defined(EMU_F68K)
|
||||
SekCycleCnt += fm68k_emulate(cyc_do, 0) - cyc_do;
|
||||
#endif
|
||||
}
|
||||
|
||||
SekCyclesLeft = 0;
|
||||
|
||||
SekTrace(0);
|
||||
pevt_log_m68k_o(EVT_RUN_END);
|
||||
}
|
||||
|
||||
static void SekRunS68k(unsigned int to)
|
||||
{
|
||||
int cyc_do;
|
||||
|
||||
SekCycleAimS68k = to;
|
||||
if ((cyc_do = SekCycleAimS68k - SekCycleCntS68k) <= 0)
|
||||
return;
|
||||
|
||||
if (SekShouldInterrupt())
|
||||
Pico_mcd->m.s68k_poll_a = 0;
|
||||
|
||||
SekCycleCntS68k += cyc_do;
|
||||
#if defined(EMU_C68K)
|
||||
PicoCpuCS68k.cycles = cyc_do;
|
||||
CycloneRun(&PicoCpuCS68k);
|
||||
SekCycleCntS68k -= PicoCpuCS68k.cycles;
|
||||
#elif defined(EMU_M68K)
|
||||
m68k_set_context(&PicoCpuMS68k);
|
||||
SekCycleCntS68k += m68k_execute(cyc_do) - cyc_do;
|
||||
m68k_set_context(&PicoCpuMM68k);
|
||||
#elif defined(EMU_F68K)
|
||||
g_m68kcontext = &PicoCpuFS68k;
|
||||
SekCycleCntS68k += fm68k_emulate(cyc_do, 0) - cyc_do;
|
||||
g_m68kcontext = &PicoCpuFM68k;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void pcd_set_cycle_mult(void)
|
||||
{
|
||||
// ~1.63 for NTSC, ~1.645 for PAL
|
||||
if (Pico.m.pal)
|
||||
mcd_m68k_cycle_mult = ((12500000ull << 16) / (50*312*488));
|
||||
else
|
||||
mcd_m68k_cycle_mult = ((12500000ull << 16) / (60*262*488)) + 1;
|
||||
}
|
||||
|
||||
unsigned int pcd_cycles_m68k_to_s68k(unsigned int c)
|
||||
{
|
||||
return (long long)c * mcd_m68k_cycle_mult >> 16;
|
||||
}
|
||||
|
||||
/* events */
|
||||
static void pcd_cdc_event(unsigned int now)
|
||||
{
|
||||
// 75Hz CDC update
|
||||
cdd_update();
|
||||
|
||||
/* check if a new CDD command has been processed */
|
||||
if (!(Pico_mcd->s68k_regs[0x4b] & 0xf0))
|
||||
{
|
||||
/* reset CDD command wait flag */
|
||||
Pico_mcd->s68k_regs[0x4b] = 0xf0;
|
||||
|
||||
if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) {
|
||||
elprintf(EL_INTS|EL_CD, "s68k: cdd irq 4");
|
||||
SekInterruptS68k(4);
|
||||
}
|
||||
}
|
||||
|
||||
pcd_event_schedule(now, PCD_EVENT_CDC, 12500000/75);
|
||||
}
|
||||
|
||||
static void pcd_int3_timer_event(unsigned int now)
|
||||
{
|
||||
if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN3) {
|
||||
elprintf(EL_INTS|EL_CD, "s68k: timer irq 3");
|
||||
SekInterruptS68k(3);
|
||||
}
|
||||
|
||||
if (Pico_mcd->s68k_regs[0x31] != 0)
|
||||
pcd_event_schedule(now, PCD_EVENT_TIMER3,
|
||||
Pico_mcd->s68k_regs[0x31] * 384);
|
||||
}
|
||||
|
||||
static void pcd_dma_event(unsigned int now)
|
||||
{
|
||||
cdc_dma_update();
|
||||
}
|
||||
|
||||
typedef void (event_cb)(unsigned int now);
|
||||
|
||||
/* times are in s68k (12.5MHz) cycles */
|
||||
unsigned int pcd_event_times[PCD_EVENT_COUNT];
|
||||
static unsigned int event_time_next;
|
||||
static event_cb *pcd_event_cbs[PCD_EVENT_COUNT] = {
|
||||
[PCD_EVENT_CDC] = pcd_cdc_event,
|
||||
[PCD_EVENT_TIMER3] = pcd_int3_timer_event,
|
||||
[PCD_EVENT_GFX] = gfx_update,
|
||||
[PCD_EVENT_DMA] = pcd_dma_event,
|
||||
};
|
||||
|
||||
void pcd_event_schedule(unsigned int now, enum pcd_event event, int after)
|
||||
{
|
||||
unsigned int when;
|
||||
|
||||
when = now + after;
|
||||
if (when == 0) {
|
||||
// event cancelled
|
||||
pcd_event_times[event] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
when |= 1;
|
||||
|
||||
elprintf(EL_CD, "cd: new event #%u %u->%u", event, now, when);
|
||||
pcd_event_times[event] = when;
|
||||
|
||||
if (event_time_next == 0 || CYCLES_GT(event_time_next, when))
|
||||
event_time_next = when;
|
||||
}
|
||||
|
||||
void pcd_event_schedule_s68k(enum pcd_event event, int after)
|
||||
{
|
||||
if (SekCyclesLeftS68k > after)
|
||||
SekEndRunS68k(after);
|
||||
|
||||
pcd_event_schedule(SekCyclesDoneS68k(), event, after);
|
||||
}
|
||||
|
||||
static void pcd_run_events(unsigned int until)
|
||||
{
|
||||
int oldest, oldest_diff, time;
|
||||
int i, diff;
|
||||
|
||||
while (1) {
|
||||
oldest = -1, oldest_diff = 0x7fffffff;
|
||||
|
||||
for (i = 0; i < PCD_EVENT_COUNT; i++) {
|
||||
if (pcd_event_times[i]) {
|
||||
diff = pcd_event_times[i] - until;
|
||||
if (diff < oldest_diff) {
|
||||
oldest_diff = diff;
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldest_diff <= 0) {
|
||||
time = pcd_event_times[oldest];
|
||||
pcd_event_times[oldest] = 0;
|
||||
elprintf(EL_CD, "cd: run event #%d %u", oldest, time);
|
||||
pcd_event_cbs[oldest](time);
|
||||
}
|
||||
else if (oldest_diff < 0x7fffffff) {
|
||||
event_time_next = pcd_event_times[oldest];
|
||||
break;
|
||||
}
|
||||
else {
|
||||
event_time_next = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldest != -1)
|
||||
elprintf(EL_CD, "cd: next event #%d at %u",
|
||||
oldest, event_time_next);
|
||||
}
|
||||
|
||||
int pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync)
|
||||
{
|
||||
#define now SekCycleCntS68k
|
||||
unsigned int s68k_target;
|
||||
unsigned int target;
|
||||
|
||||
target = m68k_target - mcd_m68k_cycle_base;
|
||||
s68k_target = mcd_s68k_cycle_base +
|
||||
((unsigned long long)target * mcd_m68k_cycle_mult >> 16);
|
||||
|
||||
elprintf(EL_CD, "s68k sync to %u, %u->%u",
|
||||
m68k_target, now, s68k_target);
|
||||
|
||||
if (Pico_mcd->m.busreq != 1) { /* busreq/reset */
|
||||
SekCycleCntS68k = SekCycleAimS68k = s68k_target;
|
||||
pcd_run_events(m68k_target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (CYCLES_GT(s68k_target, now)) {
|
||||
if (event_time_next && CYCLES_GE(now, event_time_next))
|
||||
pcd_run_events(now);
|
||||
|
||||
target = s68k_target;
|
||||
if (event_time_next && CYCLES_GT(target, event_time_next))
|
||||
target = event_time_next;
|
||||
|
||||
SekRunS68k(target);
|
||||
if (m68k_poll_sync && Pico_mcd->m.m68k_poll_cnt == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return s68k_target - now;
|
||||
#undef now
|
||||
}
|
||||
|
||||
#define pcd_run_cpus_normal pcd_run_cpus
|
||||
//#define pcd_run_cpus_lockstep pcd_run_cpus
|
||||
|
||||
static void SekSyncM68k(void);
|
||||
|
||||
void pcd_run_cpus_normal(int m68k_cycles)
|
||||
{
|
||||
SekCycleAim += m68k_cycles;
|
||||
if (SekShouldInterrupt() || Pico_mcd->m.m68k_poll_cnt < 12)
|
||||
Pico_mcd->m.m68k_poll_cnt = 0;
|
||||
else if (Pico_mcd->m.m68k_poll_cnt >= 16) {
|
||||
int s68k_left = pcd_sync_s68k(SekCycleAim, 1);
|
||||
if (s68k_left <= 0) {
|
||||
elprintf(EL_CDPOLL, "m68k poll [%02x] x%d @%06x",
|
||||
Pico_mcd->m.m68k_poll_a, Pico_mcd->m.m68k_poll_cnt, SekPc);
|
||||
SekCycleCnt = SekCycleAim;
|
||||
return;
|
||||
}
|
||||
SekCycleCnt = SekCycleAim - (s68k_left * 40220 >> 16);
|
||||
}
|
||||
|
||||
while (CYCLES_GT(SekCycleAim, SekCycleCnt)) {
|
||||
SekRunM68kOnce();
|
||||
if (Pico_mcd->m.need_sync) {
|
||||
Pico_mcd->m.need_sync = 0;
|
||||
pcd_sync_s68k(SekCycleCnt, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcd_run_cpus_lockstep(int m68k_cycles)
|
||||
{
|
||||
unsigned int target = SekCycleAim + m68k_cycles;
|
||||
do {
|
||||
SekCycleAim += 8;
|
||||
SekSyncM68k();
|
||||
pcd_sync_s68k(SekCycleAim, 0);
|
||||
} while (CYCLES_GT(target, SekCycleAim));
|
||||
|
||||
SekCycleAim = target;
|
||||
}
|
||||
|
||||
#define PICO_CD
|
||||
#define CPUS_RUN(m68k_cycles) \
|
||||
pcd_run_cpus(m68k_cycles)
|
||||
|
||||
#include "../pico_cmn.inc"
|
||||
|
||||
|
||||
void pcd_prepare_frame(void)
|
||||
{
|
||||
pcd_set_cycle_mult();
|
||||
|
||||
// need this because we can't have direct mapping between
|
||||
// master<->slave cycle counters because of overflows
|
||||
mcd_m68k_cycle_base = SekCycleAim;
|
||||
mcd_s68k_cycle_base = SekCycleAimS68k;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoFrameMCD(void)
|
||||
{
|
||||
PicoFrameStart();
|
||||
|
||||
pcd_prepare_frame();
|
||||
PicoFrameHints();
|
||||
}
|
||||
|
||||
void pcd_state_loaded(void)
|
||||
{
|
||||
unsigned int cycles;
|
||||
int diff;
|
||||
|
||||
pcd_set_cycle_mult();
|
||||
pcd_state_loaded_mem();
|
||||
|
||||
memset(Pico_mcd->pcm_mixbuf, 0, sizeof(Pico_mcd->pcm_mixbuf));
|
||||
Pico_mcd->pcm_mixbuf_dirty = 0;
|
||||
Pico_mcd->pcm_mixpos = 0;
|
||||
Pico_mcd->pcm_regs_dirty = 1;
|
||||
|
||||
// old savestates..
|
||||
cycles = pcd_cycles_m68k_to_s68k(SekCycleAim);
|
||||
diff = cycles - SekCycleAimS68k;
|
||||
if (diff < -1000 || diff > 1000) {
|
||||
SekCycleCntS68k = SekCycleAimS68k = cycles;
|
||||
}
|
||||
if (pcd_event_times[PCD_EVENT_CDC] == 0) {
|
||||
pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_CDC, 12500000/75);
|
||||
|
||||
if (Pico_mcd->s68k_regs[0x31])
|
||||
pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_TIMER3,
|
||||
Pico_mcd->s68k_regs[0x31] * 384);
|
||||
}
|
||||
|
||||
diff = cycles - Pico_mcd->pcm.update_cycles;
|
||||
if ((unsigned int)diff > 12500000/50)
|
||||
Pico_mcd->pcm.update_cycles = cycles;
|
||||
|
||||
// reschedule
|
||||
event_time_next = 0;
|
||||
pcd_run_events(SekCycleCntS68k);
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2007
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
|
||||
unsigned char formatted_bram[4*0x10] =
|
||||
{
|
||||
#if 0
|
||||
0x00, 0xd4, 0x63, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x53, 0xd2, 0xf5, 0x3a, 0x48, 0x50, 0x35, 0x0f,
|
||||
0x47, 0x14, 0xf5, 0x7e, 0x5c, 0xd4, 0xf3, 0x03, 0x00, 0x03, 0x12, 0x00, 0x0a, 0xff, 0xca, 0xa6,
|
||||
0xf5, 0x27, 0xed, 0x22, 0x47, 0xfa, 0x22, 0x96, 0x6c, 0xa5, 0x88, 0x14, 0x48, 0x48, 0x0a, 0xbb,
|
||||
#endif
|
||||
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||
0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x53, 0x45, 0x47, 0x41, 0x5f, 0x43, 0x44, 0x5f, 0x52, 0x4f, 0x4d, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x52, 0x41, 0x4d, 0x5f, 0x43, 0x41, 0x52, 0x54, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5f, 0x5f, 0x5f,
|
||||
// SEGA_CD_ROM.....RAM_CARTRIDGE___
|
||||
};
|
||||
|
||||
|
||||
// offs | 2Mbit | 1Mbit |
|
||||
// 0 | [ 2M | unused |
|
||||
// 128K | bit ] | bank0 |
|
||||
// 256K | unused | bank1 |
|
||||
|
||||
#ifndef _ASM_MISC_C
|
||||
PICO_INTERNAL_ASM void wram_2M_to_1M(unsigned char *m)
|
||||
{
|
||||
unsigned short *m1M_b0, *m1M_b1;
|
||||
unsigned int i, tmp, *m2M;
|
||||
|
||||
m2M = (unsigned int *) (m + 0x40000);
|
||||
m1M_b0 = (unsigned short *) m2M;
|
||||
m1M_b1 = (unsigned short *) (m + 0x60000);
|
||||
|
||||
for (i = 0x40000/4; i; i--)
|
||||
{
|
||||
tmp = *(--m2M);
|
||||
*(--m1M_b0) = tmp;
|
||||
*(--m1M_b1) = tmp >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
PICO_INTERNAL_ASM void wram_1M_to_2M(unsigned char *m)
|
||||
{
|
||||
unsigned short *m1M_b0, *m1M_b1;
|
||||
unsigned int i, tmp, *m2M;
|
||||
|
||||
m2M = (unsigned int *) m;
|
||||
m1M_b0 = (unsigned short *) (m + 0x20000);
|
||||
m1M_b1 = (unsigned short *) (m + 0x40000);
|
||||
|
||||
for (i = 0x40000/4; i; i--)
|
||||
{
|
||||
tmp = *m1M_b0++ | (*m1M_b1++ << 16);
|
||||
*m2M++ = tmp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Emulation routines for the RF5C164 PCM chip
|
||||
* (C) notaz, 2007, 2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
|
||||
#define PCM_STEP_SHIFT 11
|
||||
|
||||
void pcd_pcm_write(unsigned int a, unsigned int d)
|
||||
{
|
||||
unsigned int cycles = SekCyclesDoneS68k();
|
||||
if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384)
|
||||
pcd_pcm_sync(cycles);
|
||||
|
||||
if (a < 7)
|
||||
{
|
||||
Pico_mcd->pcm.ch[Pico_mcd->pcm.cur_ch].regs[a] = d;
|
||||
}
|
||||
else if (a == 7) // control register
|
||||
{
|
||||
if (d & 0x40)
|
||||
Pico_mcd->pcm.cur_ch = d & 7;
|
||||
else
|
||||
Pico_mcd->pcm.bank = d & 0xf;
|
||||
Pico_mcd->pcm.control = d;
|
||||
elprintf(EL_CD, "pcm control %02x", Pico_mcd->pcm.control);
|
||||
}
|
||||
else if (a == 8)
|
||||
{
|
||||
Pico_mcd->pcm.enabled = ~d;
|
||||
}
|
||||
Pico_mcd->pcm_regs_dirty = 1;
|
||||
}
|
||||
|
||||
unsigned int pcd_pcm_read(unsigned int a)
|
||||
{
|
||||
unsigned int d, cycles = SekCyclesDoneS68k();
|
||||
if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384)
|
||||
pcd_pcm_sync(cycles);
|
||||
|
||||
d = Pico_mcd->pcm.ch[(a >> 1) & 7].addr >> PCM_STEP_SHIFT;
|
||||
if (a & 1)
|
||||
d >>= 8;
|
||||
|
||||
return d & 0xff;
|
||||
}
|
||||
|
||||
void pcd_pcm_sync(unsigned int to)
|
||||
{
|
||||
unsigned int cycles = Pico_mcd->pcm.update_cycles;
|
||||
int mul_l, mul_r, inc, smp;
|
||||
struct pcm_chan *ch;
|
||||
unsigned int addr;
|
||||
int c, s, steps;
|
||||
int enabled;
|
||||
int *out;
|
||||
|
||||
if ((int)(to - cycles) < 384)
|
||||
return;
|
||||
|
||||
steps = (to - cycles) / 384;
|
||||
if (Pico_mcd->pcm_mixpos + steps > PCM_MIXBUF_LEN)
|
||||
// shouldn't happen, but occasionally does
|
||||
steps = PCM_MIXBUF_LEN - Pico_mcd->pcm_mixpos;
|
||||
|
||||
// PCM disabled or all channels off
|
||||
enabled = Pico_mcd->pcm.enabled;
|
||||
if (!(Pico_mcd->pcm.control & 0x80))
|
||||
enabled = 0;
|
||||
if (!enabled && !Pico_mcd->pcm_regs_dirty)
|
||||
goto end;
|
||||
|
||||
out = Pico_mcd->pcm_mixbuf + Pico_mcd->pcm_mixpos * 2;
|
||||
Pico_mcd->pcm_mixbuf_dirty = 1;
|
||||
Pico_mcd->pcm_regs_dirty = 0;
|
||||
|
||||
for (c = 0; c < 8; c++)
|
||||
{
|
||||
ch = &Pico_mcd->pcm.ch[c];
|
||||
|
||||
if (!(enabled & (1 << c))) {
|
||||
ch->addr = ch->regs[6] << (PCM_STEP_SHIFT + 8);
|
||||
continue; // channel disabled
|
||||
}
|
||||
|
||||
addr = ch->addr;
|
||||
inc = *(unsigned short *)&ch->regs[2];
|
||||
mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1);
|
||||
mul_r = ((int)ch->regs[0] * (ch->regs[1] >> 4)) >> (5+1);
|
||||
|
||||
for (s = 0; s < steps; s++, addr = (addr + inc) & 0x7FFFFFF)
|
||||
{
|
||||
smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT];
|
||||
|
||||
// test for loop signal
|
||||
if (smp == 0xff)
|
||||
{
|
||||
addr = *(unsigned short *)&ch->regs[4]; // loop_addr
|
||||
smp = Pico_mcd->pcm_ram[addr];
|
||||
addr <<= PCM_STEP_SHIFT;
|
||||
if (smp == 0xff)
|
||||
break;
|
||||
}
|
||||
|
||||
if (smp & 0x80)
|
||||
smp = -(smp & 0x7f);
|
||||
|
||||
out[s*2 ] += smp * mul_l; // max 128 * 119 = 15232
|
||||
out[s*2+1] += smp * mul_r;
|
||||
}
|
||||
ch->addr = addr;
|
||||
}
|
||||
|
||||
end:
|
||||
Pico_mcd->pcm.update_cycles = cycles + steps * 384;
|
||||
Pico_mcd->pcm_mixpos += steps;
|
||||
}
|
||||
|
||||
void pcd_pcm_update(int *buf32, int length, int stereo)
|
||||
{
|
||||
int step, *pcm;
|
||||
int p = 0;
|
||||
|
||||
pcd_pcm_sync(SekCyclesDoneS68k());
|
||||
|
||||
if (!Pico_mcd->pcm_mixbuf_dirty || !(PicoOpt & POPT_EN_MCD_PCM))
|
||||
goto out;
|
||||
|
||||
step = (Pico_mcd->pcm_mixpos << 16) / length;
|
||||
pcm = Pico_mcd->pcm_mixbuf;
|
||||
|
||||
if (stereo) {
|
||||
while (length-- > 0) {
|
||||
*buf32++ += pcm[0];
|
||||
*buf32++ += pcm[1];
|
||||
|
||||
p += step;
|
||||
pcm += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (length-- > 0) {
|
||||
// mostly unused
|
||||
*buf32++ += pcm[0];
|
||||
|
||||
p += step;
|
||||
pcm += (p >> 16) * 2;
|
||||
p &= 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
memset(Pico_mcd->pcm_mixbuf, 0,
|
||||
Pico_mcd->pcm_mixpos * 2 * sizeof(Pico_mcd->pcm_mixbuf[0]));
|
||||
|
||||
out:
|
||||
Pico_mcd->pcm_mixbuf_dirty = 0;
|
||||
Pico_mcd->pcm_mixpos = 0;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2007
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
|
||||
|
||||
unsigned int SekCycleCntS68k;
|
||||
unsigned int SekCycleAimS68k;
|
||||
|
||||
|
||||
/* context */
|
||||
// Cyclone 68000
|
||||
#ifdef EMU_C68K
|
||||
struct Cyclone PicoCpuCS68k;
|
||||
#endif
|
||||
// MUSASHI 68000
|
||||
#ifdef EMU_M68K
|
||||
m68ki_cpu_core PicoCpuMS68k;
|
||||
#endif
|
||||
// FAME 68000
|
||||
#ifdef EMU_F68K
|
||||
M68K_CONTEXT PicoCpuFS68k;
|
||||
#endif
|
||||
|
||||
|
||||
static int new_irq_level(int level)
|
||||
{
|
||||
int level_new = 0, irqs;
|
||||
Pico_mcd->m.s68k_pend_ints &= ~(1 << level);
|
||||
irqs = Pico_mcd->m.s68k_pend_ints;
|
||||
irqs &= Pico_mcd->s68k_regs[0x33];
|
||||
while ((irqs >>= 1)) level_new++;
|
||||
|
||||
return level_new;
|
||||
}
|
||||
|
||||
#ifdef EMU_C68K
|
||||
// interrupt acknowledgement
|
||||
static int SekIntAckS68k(int level)
|
||||
{
|
||||
int level_new = new_irq_level(level);
|
||||
|
||||
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
|
||||
PicoCpuCS68k.irq = level_new;
|
||||
return CYCLONE_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
static void SekResetAckS68k(void)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "s68k: Reset encountered @ %06x", SekPcS68k);
|
||||
}
|
||||
|
||||
static int SekUnrecognizedOpcodeS68k(void)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "Unrecognized Opcode @ %06x", SekPcS68k);
|
||||
//exit(1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef EMU_M68K
|
||||
static int SekIntAckMS68k(int level)
|
||||
{
|
||||
#ifndef EMU_CORE_DEBUG
|
||||
int level_new = new_irq_level(level);
|
||||
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
|
||||
CPU_INT_LEVEL = level_new << 8;
|
||||
#else
|
||||
CPU_INT_LEVEL = 0;
|
||||
#endif
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef EMU_F68K
|
||||
static void SekIntAckFS68k(unsigned level)
|
||||
{
|
||||
int level_new = new_irq_level(level);
|
||||
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
|
||||
#ifndef EMU_CORE_DEBUG
|
||||
PicoCpuFS68k.interrupts[0] = level_new;
|
||||
#else
|
||||
{
|
||||
extern int dbg_irq_level_sub;
|
||||
dbg_irq_level_sub = level_new;
|
||||
PicoCpuFS68k.interrupts[0] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
PICO_INTERNAL void SekInitS68k(void)
|
||||
{
|
||||
#ifdef EMU_C68K
|
||||
// CycloneInit();
|
||||
memset(&PicoCpuCS68k,0,sizeof(PicoCpuCS68k));
|
||||
PicoCpuCS68k.IrqCallback=SekIntAckS68k;
|
||||
PicoCpuCS68k.ResetCallback=SekResetAckS68k;
|
||||
PicoCpuCS68k.UnrecognizedCallback=SekUnrecognizedOpcodeS68k;
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
{
|
||||
// Musashi is not very context friendly..
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
m68k_set_context(&PicoCpuMS68k);
|
||||
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
|
||||
m68k_init();
|
||||
m68k_set_int_ack_callback(SekIntAckMS68k);
|
||||
// m68k_pulse_reset(); // not yet, memmap is not set up
|
||||
m68k_set_context(oldcontext);
|
||||
}
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
{
|
||||
void *oldcontext = g_m68kcontext;
|
||||
g_m68kcontext = &PicoCpuFS68k;
|
||||
memset(&PicoCpuFS68k, 0, sizeof(PicoCpuFS68k));
|
||||
fm68k_init();
|
||||
PicoCpuFS68k.iack_handler = SekIntAckFS68k;
|
||||
PicoCpuFS68k.sr = 0x2704; // Z flag
|
||||
g_m68kcontext = oldcontext;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reset the 68000:
|
||||
PICO_INTERNAL int SekResetS68k(void)
|
||||
{
|
||||
if (Pico.rom==NULL) return 1;
|
||||
|
||||
#ifdef EMU_C68K
|
||||
CycloneReset(&PicoCpuCS68k);
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
{
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
|
||||
m68k_set_context(&PicoCpuMS68k);
|
||||
m68ki_cpu.sp[0]=0;
|
||||
m68k_set_irq(0);
|
||||
m68k_pulse_reset();
|
||||
m68k_set_context(oldcontext);
|
||||
}
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
{
|
||||
void *oldcontext = g_m68kcontext;
|
||||
g_m68kcontext = &PicoCpuFS68k;
|
||||
fm68k_reset();
|
||||
g_m68kcontext = oldcontext;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_INTERNAL int SekInterruptS68k(int irq)
|
||||
{
|
||||
int irqs, real_irq = 1;
|
||||
Pico_mcd->m.s68k_pend_ints |= 1 << irq;
|
||||
irqs = Pico_mcd->m.s68k_pend_ints >> 1;
|
||||
while ((irqs >>= 1)) real_irq++;
|
||||
|
||||
#ifdef EMU_CORE_DEBUG
|
||||
{
|
||||
extern int dbg_irq_level_sub;
|
||||
dbg_irq_level_sub=real_irq;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef EMU_C68K
|
||||
PicoCpuCS68k.irq=real_irq;
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
m68k_set_context(&PicoCpuMS68k);
|
||||
m68k_set_irq(real_irq);
|
||||
m68k_set_context(oldcontext);
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
PicoCpuFS68k.interrupts[0]=real_irq;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SekInterruptClearS68k(int irq)
|
||||
{
|
||||
int level_new = new_irq_level(irq);
|
||||
|
||||
#ifdef EMU_C68K
|
||||
PicoCpuCS68k.irq = level_new;
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
CPU_INT_LEVEL = level_new << 8;
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
PicoCpuFS68k.interrupts[0] = level_new;
|
||||
#endif
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
* tile renderer
|
||||
* (C) notaz, 2006-2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
|
||||
#define START_ROW 0 // which row of tiles to start rendering at?
|
||||
#define END_ROW 28 // ..end
|
||||
|
||||
#define TILE_ROWS END_ROW-START_ROW
|
||||
|
||||
// note: this is not implemented in ARM asm
|
||||
#if defined(DRAW2_OVERRIDE_LINE_WIDTH)
|
||||
#define LINE_WIDTH DRAW2_OVERRIDE_LINE_WIDTH
|
||||
#else
|
||||
#define LINE_WIDTH 328
|
||||
#endif
|
||||
|
||||
static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];
|
||||
unsigned char *PicoDraw2FB = PicoDraw2FB_;
|
||||
|
||||
static int HighCache2A[41*(TILE_ROWS+1)+1+1]; // caches for high layers
|
||||
static int HighCache2B[41*(TILE_ROWS+1)+1+1];
|
||||
|
||||
unsigned short *PicoCramHigh=Pico.cram; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now)
|
||||
void (*PicoPrepareCram)()=0; // prepares PicoCramHigh for renderer to use
|
||||
|
||||
|
||||
// stuff available in asm:
|
||||
#ifdef _ASM_DRAW_C
|
||||
void BackFillFull(int reg7);
|
||||
void DrawLayerFull(int plane, int *hcache, int planestart, int planeend);
|
||||
void DrawTilesFromCacheF(int *hc);
|
||||
void DrawWindowFull(int start, int end, int prio);
|
||||
void DrawSpriteFull(unsigned int *sprite);
|
||||
#else
|
||||
|
||||
|
||||
static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal)
|
||||
{
|
||||
unsigned int pack=0; unsigned int t=0, blank = 1;
|
||||
int i;
|
||||
|
||||
for(i=8; i; i--, addr+=2, pd += LINE_WIDTH) {
|
||||
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
|
||||
if(!pack) continue;
|
||||
|
||||
t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
|
||||
t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);
|
||||
t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);
|
||||
t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t )|pal);
|
||||
t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);
|
||||
t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);
|
||||
t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);
|
||||
t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);
|
||||
blank = 0;
|
||||
}
|
||||
|
||||
return blank; // Tile blank?
|
||||
}
|
||||
|
||||
static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal)
|
||||
{
|
||||
unsigned int pack=0; unsigned int t=0, blank = 1;
|
||||
int i;
|
||||
|
||||
for(i=8; i; i--, addr+=2, pd += LINE_WIDTH) {
|
||||
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
|
||||
if(!pack) continue;
|
||||
|
||||
t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
|
||||
t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);
|
||||
t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);
|
||||
t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);
|
||||
t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t )|pal);
|
||||
t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);
|
||||
t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);
|
||||
t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);
|
||||
blank = 0;
|
||||
}
|
||||
return blank; // Tile blank?
|
||||
}
|
||||
|
||||
static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal)
|
||||
{
|
||||
unsigned int pack=0; unsigned int t=0, blank = 1;
|
||||
int i;
|
||||
|
||||
addr+=14;
|
||||
for(i=8; i; i--, addr-=2, pd += LINE_WIDTH) {
|
||||
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
|
||||
if(!pack) continue;
|
||||
|
||||
t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
|
||||
t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);
|
||||
t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);
|
||||
t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t )|pal);
|
||||
t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);
|
||||
t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);
|
||||
t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);
|
||||
t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);
|
||||
blank = 0;
|
||||
}
|
||||
|
||||
return blank; // Tile blank?
|
||||
}
|
||||
|
||||
static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal)
|
||||
{
|
||||
unsigned int pack=0; unsigned int t=0, blank = 1;
|
||||
int i;
|
||||
|
||||
addr+=14;
|
||||
for(i=8; i; i--, addr-=2, pd += LINE_WIDTH) {
|
||||
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
|
||||
if(!pack) continue;
|
||||
|
||||
t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
|
||||
t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);
|
||||
t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);
|
||||
t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);
|
||||
t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t )|pal);
|
||||
t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);
|
||||
t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);
|
||||
t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);
|
||||
blank = 0;
|
||||
}
|
||||
return blank; // Tile blank?
|
||||
}
|
||||
|
||||
|
||||
// start: (tile_start<<16)|row_start, end: [same]
|
||||
static void DrawWindowFull(int start, int end, int prio)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
int nametab, nametab_step, trow, tilex, blank=-1, code;
|
||||
unsigned char *scrpos = PicoDraw2FB;
|
||||
int tile_start, tile_end; // in cells
|
||||
|
||||
// parse ranges
|
||||
tile_start = start>>16;
|
||||
tile_end = end>>16;
|
||||
start = start<<16>>16;
|
||||
end = end<<16>>16;
|
||||
|
||||
// Find name table line:
|
||||
if (pvid->reg[12]&1)
|
||||
{
|
||||
nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode
|
||||
nametab_step = 1<<6;
|
||||
}
|
||||
else
|
||||
{
|
||||
nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode
|
||||
nametab_step = 1<<5;
|
||||
}
|
||||
nametab += nametab_step*start;
|
||||
|
||||
// check priority
|
||||
code=Pico.vram[nametab+tile_start];
|
||||
if ((code>>15) != prio) return; // hack: just assume that whole window uses same priority
|
||||
|
||||
scrpos+=8*LINE_WIDTH+8;
|
||||
scrpos+=8*LINE_WIDTH*(start-START_ROW);
|
||||
|
||||
// do a window until we reach planestart row
|
||||
for(trow = start; trow < end; trow++, nametab+=nametab_step) { // current tile row
|
||||
for (tilex=tile_start; tilex<tile_end; tilex++)
|
||||
{
|
||||
int code,addr,zero=0;
|
||||
// unsigned short *pal=NULL;
|
||||
unsigned char pal;
|
||||
|
||||
code=Pico.vram[nametab+tilex];
|
||||
if (code==blank) continue;
|
||||
|
||||
// Get tile address/2:
|
||||
addr=(code&0x7ff)<<4;
|
||||
|
||||
// pal=PicoCramHigh+((code>>9)&0x30);
|
||||
pal=(unsigned char)((code>>9)&0x30);
|
||||
|
||||
switch((code>>11)&3) {
|
||||
case 0: zero=TileXnormYnorm(scrpos+(tilex<<3),addr,pal); break;
|
||||
case 1: zero=TileXflipYnorm(scrpos+(tilex<<3),addr,pal); break;
|
||||
case 2: zero=TileXnormYflip(scrpos+(tilex<<3),addr,pal); break;
|
||||
case 3: zero=TileXflipYflip(scrpos+(tilex<<3),addr,pal); break;
|
||||
}
|
||||
if(zero) blank=code; // We know this tile is blank now
|
||||
}
|
||||
|
||||
scrpos += LINE_WIDTH*8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DrawLayerFull(int plane, int *hcache, int planestart, int planeend)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
static char shift[4]={5,6,6,7}; // 32,64 or 128 sized tilemaps
|
||||
int width, height, ymask, htab;
|
||||
int nametab, hscroll=0, vscroll, cells;
|
||||
unsigned char *scrpos;
|
||||
int blank=-1, xmask, nametab_row, trow;
|
||||
|
||||
// parse ranges
|
||||
cells = (planeend>>16)-(planestart>>16);
|
||||
planestart = planestart<<16>>16;
|
||||
planeend = planeend<<16>>16;
|
||||
|
||||
// Work out the Tiles to draw
|
||||
|
||||
htab=pvid->reg[13]<<9; // Horizontal scroll table address
|
||||
// if ( pvid->reg[11]&2) htab+=Scanline<<1; // Offset by line
|
||||
// if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile
|
||||
htab+=plane; // A or B
|
||||
|
||||
if(!(pvid->reg[11]&3)) { // full screen scroll
|
||||
// Get horizontal scroll value
|
||||
hscroll=Pico.vram[htab&0x7fff];
|
||||
htab = 0; // this marks that we don't have to update scroll value
|
||||
}
|
||||
|
||||
// Work out the name table size: 32 64 or 128 tiles (0-3)
|
||||
width=pvid->reg[16];
|
||||
height=(width>>4)&3; width&=3;
|
||||
|
||||
xmask=(1<<shift[width ])-1; // X Mask in tiles
|
||||
ymask=(height<<5)|0x1f; // Y Mask in tiles
|
||||
if(width == 1) ymask&=0x3f;
|
||||
else if(width>1) ymask =0x1f;
|
||||
|
||||
// Find name table:
|
||||
if (plane==0) nametab=(pvid->reg[2]&0x38)<< 9; // A
|
||||
else nametab=(pvid->reg[4]&0x07)<<12; // B
|
||||
|
||||
scrpos = PicoDraw2FB;
|
||||
scrpos+=8*LINE_WIDTH*(planestart-START_ROW);
|
||||
|
||||
// Get vertical scroll value:
|
||||
vscroll=Pico.vsram[plane]&0x1ff;
|
||||
scrpos+=(8-(vscroll&7))*LINE_WIDTH;
|
||||
if(vscroll&7) planeend++; // we have vertically clipped tiles due to vscroll, so we need 1 more row
|
||||
|
||||
*hcache++ = 8-(vscroll&7); // push y-offset to tilecache
|
||||
|
||||
|
||||
for(trow = planestart; trow < planeend; trow++) { // current tile row
|
||||
int cellc=cells,tilex,dx;
|
||||
|
||||
// Find the tile row in the name table
|
||||
//ts.line=(vscroll+Scanline)&ymask;
|
||||
//ts.nametab+=(ts.line>>3)<<shift[width];
|
||||
nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row
|
||||
|
||||
// update hscroll if needed
|
||||
if(htab) {
|
||||
int htaddr=htab+(trow<<4);
|
||||
if(trow) htaddr-=(vscroll&7)<<1;
|
||||
hscroll=Pico.vram[htaddr&0x7fff];
|
||||
}
|
||||
|
||||
// Draw tiles across screen:
|
||||
tilex=(-hscroll)>>3;
|
||||
dx=((hscroll-1)&7)+1;
|
||||
if(dx != 8) cellc++; // have hscroll, do more cells
|
||||
|
||||
for (; cellc; dx+=8,tilex++,cellc--)
|
||||
{
|
||||
int code=0,addr=0,zero=0;
|
||||
// unsigned short *pal=NULL;
|
||||
unsigned char pal;
|
||||
|
||||
code=Pico.vram[nametab_row+(tilex&xmask)];
|
||||
if (code==blank) continue;
|
||||
|
||||
if (code>>15) { // high priority tile
|
||||
*hcache++ = code|(dx<<16)|(trow<<27); // cache it
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get tile address/2:
|
||||
addr=(code&0x7ff)<<4;
|
||||
|
||||
// pal=PicoCramHigh+((code>>9)&0x30);
|
||||
pal=(unsigned char)((code>>9)&0x30);
|
||||
|
||||
switch((code>>11)&3) {
|
||||
case 0: zero=TileXnormYnorm(scrpos+dx,addr,pal); break;
|
||||
case 1: zero=TileXflipYnorm(scrpos+dx,addr,pal); break;
|
||||
case 2: zero=TileXnormYflip(scrpos+dx,addr,pal); break;
|
||||
case 3: zero=TileXflipYflip(scrpos+dx,addr,pal); break;
|
||||
}
|
||||
if(zero) blank=code; // We know this tile is blank now
|
||||
}
|
||||
|
||||
scrpos += LINE_WIDTH*8;
|
||||
}
|
||||
|
||||
*hcache = 0; // terminate cache
|
||||
}
|
||||
|
||||
|
||||
static void DrawTilesFromCacheF(int *hc)
|
||||
{
|
||||
int code, addr, zero = 0;
|
||||
unsigned int prevy=0xFFFFFFFF;
|
||||
// unsigned short *pal;
|
||||
unsigned char pal;
|
||||
short blank=-1; // The tile we know is blank
|
||||
unsigned char *scrpos = PicoDraw2FB, *pd = 0;
|
||||
|
||||
// *hcache++ = code|(dx<<16)|(trow<<27); // cache it
|
||||
scrpos+=(*hc++)*LINE_WIDTH - START_ROW*LINE_WIDTH*8;
|
||||
|
||||
while((code=*hc++)) {
|
||||
if((short)code == blank) continue;
|
||||
|
||||
// y pos
|
||||
if(((unsigned)code>>27) != prevy) {
|
||||
prevy = (unsigned)code>>27;
|
||||
pd = scrpos + prevy*LINE_WIDTH*8;
|
||||
}
|
||||
|
||||
// Get tile address/2:
|
||||
addr=(code&0x7ff)<<4;
|
||||
// pal=PicoCramHigh+((code>>9)&0x30);
|
||||
pal=(unsigned char)((code>>9)&0x30);
|
||||
|
||||
switch((code>>11)&3) {
|
||||
case 0: zero=TileXnormYnorm(pd+((code>>16)&0x1ff),addr,pal); break;
|
||||
case 1: zero=TileXflipYnorm(pd+((code>>16)&0x1ff),addr,pal); break;
|
||||
case 2: zero=TileXnormYflip(pd+((code>>16)&0x1ff),addr,pal); break;
|
||||
case 3: zero=TileXflipYflip(pd+((code>>16)&0x1ff),addr,pal); break;
|
||||
}
|
||||
|
||||
if(zero) blank=(short)code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sx and sy are coords of virtual screen with 8pix borders on top and on left
|
||||
static void DrawSpriteFull(unsigned int *sprite)
|
||||
{
|
||||
int width=0,height=0;
|
||||
// unsigned short *pal=NULL;
|
||||
unsigned char pal;
|
||||
int tile,code,tdeltax,tdeltay;
|
||||
unsigned char *scrpos;
|
||||
int sx, sy;
|
||||
|
||||
sy=sprite[0];
|
||||
height=sy>>24;
|
||||
sy=(sy&0x1ff)-0x78; // Y
|
||||
width=(height>>2)&3; height&=3;
|
||||
width++; height++; // Width and height in tiles
|
||||
|
||||
code=sprite[1];
|
||||
sx=((code>>16)&0x1ff)-0x78; // X
|
||||
|
||||
tile=code&0x7ff; // Tile number
|
||||
tdeltax=height; // Delta to increase tile by going right
|
||||
tdeltay=1; // Delta to increase tile by going down
|
||||
if (code&0x0800) { tdeltax=-tdeltax; tile+=height*(width-1); } // Flip X
|
||||
if (code&0x1000) { tdeltay=-tdeltay; tile+=height-1; } // Flip Y
|
||||
|
||||
//delta<<=4; // Delta of address
|
||||
// pal=PicoCramHigh+((code>>9)&0x30); // Get palette pointer
|
||||
pal=(unsigned char)((code>>9)&0x30);
|
||||
|
||||
// goto first vertically visible tile
|
||||
while(sy <= START_ROW*8) { sy+=8; tile+=tdeltay; height--; }
|
||||
|
||||
scrpos = PicoDraw2FB;
|
||||
scrpos+=(sy-START_ROW*8)*LINE_WIDTH;
|
||||
|
||||
for (; height > 0; height--, sy+=8, tile+=tdeltay)
|
||||
{
|
||||
int w = width, x=sx, t=tile;
|
||||
|
||||
if(sy >= END_ROW*8+8) return; // offscreen
|
||||
|
||||
for (; w; w--,x+=8,t+=tdeltax)
|
||||
{
|
||||
if(x<=0) continue;
|
||||
if(x>=328) break; // Offscreen
|
||||
|
||||
t&=0x7fff; // Clip tile address
|
||||
switch((code>>11)&3) {
|
||||
case 0: TileXnormYnorm(scrpos+x,t<<4,pal); break;
|
||||
case 1: TileXflipYnorm(scrpos+x,t<<4,pal); break;
|
||||
case 2: TileXnormYflip(scrpos+x,t<<4,pal); break;
|
||||
case 3: TileXflipYflip(scrpos+x,t<<4,pal); break;
|
||||
}
|
||||
}
|
||||
|
||||
scrpos+=8*LINE_WIDTH;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void DrawAllSpritesFull(int prio, int maxwidth)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
int table=0,maskrange=0;
|
||||
int i,u,link=0;
|
||||
unsigned int *sprites[80]; // Sprites
|
||||
int y_min=START_ROW*8, y_max=END_ROW*8; // for a simple sprite masking
|
||||
|
||||
table=pvid->reg[5]&0x7f;
|
||||
if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode
|
||||
table<<=8; // Get sprite table address/2
|
||||
|
||||
for (i=u=0; u < 80; u++)
|
||||
{
|
||||
unsigned int *sprite=NULL;
|
||||
int code, code2, sx, sy, height;
|
||||
|
||||
sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite
|
||||
|
||||
// get sprite info
|
||||
code = sprite[0];
|
||||
|
||||
// check if it is not hidden vertically
|
||||
sy = (code&0x1ff)-0x80;
|
||||
height = (((code>>24)&3)+1)<<3;
|
||||
if(sy+height <= y_min || sy > y_max) goto nextsprite;
|
||||
|
||||
// masking sprite?
|
||||
code2=sprite[1];
|
||||
sx = (code2>>16)&0x1ff;
|
||||
if(!sx) {
|
||||
int to = sy+height; // sy ~ from
|
||||
if(maskrange) {
|
||||
// try to merge with previous range
|
||||
if((maskrange>>16)+1 >= sy && (maskrange>>16) <= to && (maskrange&0xffff) < sy) sy = (maskrange&0xffff);
|
||||
else if((maskrange&0xffff)-1 <= to && (maskrange&0xffff) >= sy && (maskrange>>16) > to) to = (maskrange>>16);
|
||||
}
|
||||
// support only very simple masking (top and bottom of screen)
|
||||
if(sy <= y_min && to+1 > y_min) y_min = to+1;
|
||||
else if(to >= y_max && sy-1 < y_max) y_max = sy-1;
|
||||
else maskrange=sy|(to<<16);
|
||||
|
||||
goto nextsprite;
|
||||
}
|
||||
|
||||
// priority
|
||||
if(((code2>>15)&1) != prio) goto nextsprite; // wrong priority
|
||||
|
||||
// check if sprite is not hidden horizontally
|
||||
sx -= 0x78; // Get X coordinate + 8
|
||||
if(sx <= -8*3 || sx >= maxwidth) goto nextsprite;
|
||||
|
||||
// sprite is good, save it's index
|
||||
sprites[i++]=sprite;
|
||||
|
||||
nextsprite:
|
||||
// Find next sprite
|
||||
link=(code>>16)&0x7f;
|
||||
if(!link) break; // End of sprites
|
||||
}
|
||||
|
||||
// Go through sprites backwards:
|
||||
for (i-- ;i>=0; i--)
|
||||
{
|
||||
DrawSpriteFull(sprites[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _ASM_DRAW_C
|
||||
static void BackFillFull(int reg7)
|
||||
{
|
||||
unsigned int back;
|
||||
|
||||
// Start with a background color:
|
||||
// back=PicoCramHigh[reg7&0x3f];
|
||||
back=reg7&0x3f;
|
||||
back|=back<<8;
|
||||
back|=back<<16;
|
||||
|
||||
memset32((int *)PicoDraw2FB, back, LINE_WIDTH*(8+(END_ROW-START_ROW)*8)/4);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void DrawDisplayFull(void)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
int win, edge=0, hvwin=0; // LSb->MSb: hwin&plane, vwin&plane, full
|
||||
int planestart=START_ROW, planeend=END_ROW; // plane A start/end when window shares display with plane A (in tile rows or columns)
|
||||
int winstart=START_ROW, winend=END_ROW; // same for window
|
||||
int maxw, maxcolc; // max width and col cells
|
||||
|
||||
if(pvid->reg[12]&1) {
|
||||
maxw = 328; maxcolc = 40;
|
||||
} else {
|
||||
maxw = 264; maxcolc = 32;
|
||||
}
|
||||
|
||||
// horizontal window?
|
||||
if ((win=pvid->reg[0x12]))
|
||||
{
|
||||
hvwin=1; // hwindow shares display with plane A
|
||||
edge=win&0x1f;
|
||||
if(win == 0x80) {
|
||||
// fullscreen window
|
||||
hvwin=4;
|
||||
} else if(win < 0x80) {
|
||||
// window on the top
|
||||
if(edge <= START_ROW) hvwin=0; // window not visible in our drawing region
|
||||
else if(edge >= END_ROW) hvwin=4;
|
||||
else planestart = winend = edge;
|
||||
} else if(win > 0x80) {
|
||||
// window at the bottom
|
||||
if(edge >= END_ROW) hvwin=0;
|
||||
else planeend = winstart = edge;
|
||||
}
|
||||
}
|
||||
|
||||
// check for vertical window, but only if win is not fullscreen
|
||||
if (hvwin != 4)
|
||||
{
|
||||
win=pvid->reg[0x11];
|
||||
edge=win&0x1f;
|
||||
if (win&0x80) {
|
||||
if(!edge) hvwin=4;
|
||||
else if(edge < (maxcolc>>1)) {
|
||||
// window is on the right
|
||||
hvwin|=2;
|
||||
planeend|=edge<<17;
|
||||
winstart|=edge<<17;
|
||||
winend|=maxcolc<<16;
|
||||
}
|
||||
} else {
|
||||
if(edge >= (maxcolc>>1)) hvwin=4;
|
||||
else if(edge) {
|
||||
// window is on the left
|
||||
hvwin|=2;
|
||||
winend|=edge<<17;
|
||||
planestart|=edge<<17;
|
||||
planeend|=maxcolc<<16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hvwin==1) { winend|=maxcolc<<16; planeend|=maxcolc<<16; }
|
||||
|
||||
HighCache2A[1] = HighCache2B[1] = 0;
|
||||
if (PicoDrawMask & PDRAW_LAYERB_ON)
|
||||
DrawLayerFull(1, HighCache2B, START_ROW, (maxcolc<<16)|END_ROW);
|
||||
if (PicoDrawMask & PDRAW_LAYERA_ON) switch (hvwin)
|
||||
{
|
||||
case 4:
|
||||
// fullscreen window
|
||||
DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// we have plane A and both v and h windows
|
||||
DrawLayerFull(0, HighCache2A, planestart, planeend);
|
||||
DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 0); // h
|
||||
DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 0); // v
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 1:
|
||||
// both window and plane A visible, window is vertical XOR horizontal
|
||||
DrawLayerFull(0, HighCache2A, planestart, planeend);
|
||||
DrawWindowFull(winstart, winend, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
// fullscreen plane A
|
||||
DrawLayerFull(0, HighCache2A, START_ROW, (maxcolc<<16)|END_ROW);
|
||||
break;
|
||||
}
|
||||
if (PicoDrawMask & PDRAW_SPRITES_LOW_ON)
|
||||
DrawAllSpritesFull(0, maxw);
|
||||
|
||||
if (HighCache2B[1]) DrawTilesFromCacheF(HighCache2B);
|
||||
if (HighCache2A[1]) DrawTilesFromCacheF(HighCache2A);
|
||||
if (PicoDrawMask & PDRAW_LAYERA_ON) switch (hvwin)
|
||||
{
|
||||
case 4:
|
||||
// fullscreen window
|
||||
DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 1);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// we have plane A and both v and h windows
|
||||
DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 1); // h
|
||||
DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 1); // v
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 1:
|
||||
// both window and plane A visible, window is vertical XOR horizontal
|
||||
DrawWindowFull(winstart, winend, 1);
|
||||
break;
|
||||
}
|
||||
if (PicoDrawMask & PDRAW_SPRITES_HI_ON)
|
||||
DrawAllSpritesFull(1, maxw);
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PicoFrameFull()
|
||||
{
|
||||
pprof_start(draw);
|
||||
|
||||
// prepare cram?
|
||||
if (PicoPrepareCram) PicoPrepareCram();
|
||||
|
||||
// Draw screen:
|
||||
BackFillFull(Pico.video.reg[7]);
|
||||
if (Pico.video.reg[1] & 0x40)
|
||||
DrawDisplayFull();
|
||||
|
||||
pprof_end(draw);
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* rarely used EEPROM code
|
||||
* (C) notaz, 2007-2009
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*
|
||||
* (see Genesis Plus for Wii/GC code and docs for info,
|
||||
* full game list and better code).
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
|
||||
static unsigned int last_write = 0xffff0000;
|
||||
|
||||
// eeprom_status: LA.. s.la (L=pending SCL, A=pending SDA,
|
||||
// s=started, l=old SCL, a=old SDA)
|
||||
static void EEPROM_write_do(unsigned int d) // ???? ??la (l=SCL, a=SDA)
|
||||
{
|
||||
unsigned int sreg = Pico.m.eeprom_status, saddr = Pico.m.eeprom_addr;
|
||||
unsigned int scyc = Pico.m.eeprom_cycle, ssa = Pico.m.eeprom_slave;
|
||||
|
||||
elprintf(EL_EEPROM, "eeprom: scl/sda: %i/%i -> %i/%i, newtime=%i", (sreg&2)>>1, sreg&1,
|
||||
(d&2)>>1, d&1, SekCyclesDone() - last_write);
|
||||
saddr &= 0x1fff;
|
||||
|
||||
if(sreg & d & 2) {
|
||||
// SCL was and is still high..
|
||||
if((sreg & 1) && !(d&1)) {
|
||||
// ..and SDA went low, means it's a start command, so clear internal addr reg and clock counter
|
||||
elprintf(EL_EEPROM, "eeprom: -start-");
|
||||
//saddr = 0;
|
||||
scyc = 0;
|
||||
sreg |= 8;
|
||||
} else if(!(sreg & 1) && (d&1)) {
|
||||
// SDA went high == stop command
|
||||
elprintf(EL_EEPROM, "eeprom: -stop-");
|
||||
sreg &= ~8;
|
||||
}
|
||||
}
|
||||
else if((sreg & 8) && !(sreg & 2) && (d&2))
|
||||
{
|
||||
// we are started and SCL went high - next cycle
|
||||
scyc++; // pre-increment
|
||||
if(SRam.eeprom_type) {
|
||||
// X24C02+
|
||||
if((ssa&1) && scyc == 18) {
|
||||
scyc = 9;
|
||||
saddr++; // next address in read mode
|
||||
/*if(SRam.eeprom_type==2) saddr&=0xff; else*/ saddr&=0x1fff; // mask
|
||||
}
|
||||
else if(SRam.eeprom_type == 2 && scyc == 27) scyc = 18;
|
||||
else if(scyc == 36) scyc = 27;
|
||||
} else {
|
||||
// X24C01
|
||||
if(scyc == 18) {
|
||||
scyc = 9; // wrap
|
||||
if(saddr&1) { saddr+=2; saddr&=0xff; } // next addr in read mode
|
||||
}
|
||||
}
|
||||
elprintf(EL_EEPROM, "eeprom: scyc: %i", scyc);
|
||||
}
|
||||
else if((sreg & 8) && (sreg & 2) && !(d&2))
|
||||
{
|
||||
// we are started and SCL went low (falling edge)
|
||||
if(SRam.eeprom_type) {
|
||||
// X24C02+
|
||||
if(scyc == 9 || scyc == 18 || scyc == 27); // ACK cycles
|
||||
else if( (SRam.eeprom_type == 3 && scyc > 27) || (SRam.eeprom_type == 2 && scyc > 18) ) {
|
||||
if(!(ssa&1)) {
|
||||
// data write
|
||||
unsigned char *pm=SRam.data+saddr;
|
||||
*pm <<= 1; *pm |= d&1;
|
||||
if(scyc == 26 || scyc == 35) {
|
||||
saddr=(saddr&~0xf)|((saddr+1)&0xf); // only 4 (?) lowest bits are incremented
|
||||
elprintf(EL_EEPROM, "eeprom: write done, addr inc to: %x, last byte=%02x", saddr, *pm);
|
||||
}
|
||||
SRam.changed = 1;
|
||||
}
|
||||
} else if(scyc > 9) {
|
||||
if(!(ssa&1)) {
|
||||
// we latch another addr bit
|
||||
saddr<<=1;
|
||||
if(SRam.eeprom_type == 2) saddr&=0xff; else saddr&=0x1fff; // mask
|
||||
saddr|=d&1;
|
||||
if(scyc==17||scyc==26) {
|
||||
elprintf(EL_EEPROM, "eeprom: addr reg done: %x", saddr);
|
||||
if(scyc==17&&SRam.eeprom_type==2) { saddr&=0xff; saddr|=(ssa<<7)&0x700; } // add device bits too
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// slave address
|
||||
ssa<<=1; ssa|=d&1;
|
||||
if(scyc==8) elprintf(EL_EEPROM, "eeprom: slave done: %x", ssa);
|
||||
}
|
||||
} else {
|
||||
// X24C01
|
||||
if(scyc == 9); // ACK cycle, do nothing
|
||||
else if(scyc > 9) {
|
||||
if(!(saddr&1)) {
|
||||
// data write
|
||||
unsigned char *pm=SRam.data+(saddr>>1);
|
||||
*pm <<= 1; *pm |= d&1;
|
||||
if(scyc == 17) {
|
||||
saddr=(saddr&0xf9)|((saddr+2)&6); // only 2 lowest bits are incremented
|
||||
elprintf(EL_EEPROM, "eeprom: write done, addr inc to: %x, last byte=%02x", saddr>>1, *pm);
|
||||
}
|
||||
SRam.changed = 1;
|
||||
}
|
||||
} else {
|
||||
// we latch another addr bit
|
||||
saddr<<=1; saddr|=d&1; saddr&=0xff;
|
||||
if(scyc==8) elprintf(EL_EEPROM, "eeprom: addr done: %x", saddr>>1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sreg &= ~3; sreg |= d&3; // remember SCL and SDA
|
||||
Pico.m.eeprom_status = (unsigned char) sreg;
|
||||
Pico.m.eeprom_cycle = (unsigned char) scyc;
|
||||
Pico.m.eeprom_slave = (unsigned char) ssa;
|
||||
Pico.m.eeprom_addr = (unsigned short)saddr;
|
||||
}
|
||||
|
||||
static void EEPROM_upd_pending(unsigned int d)
|
||||
{
|
||||
unsigned int d1, sreg = Pico.m.eeprom_status;
|
||||
|
||||
sreg &= ~0xc0;
|
||||
|
||||
// SCL
|
||||
d1 = (d >> SRam.eeprom_bit_cl) & 1;
|
||||
sreg |= d1 << 7;
|
||||
|
||||
// SDA in
|
||||
d1 = (d >> SRam.eeprom_bit_in) & 1;
|
||||
sreg |= d1 << 6;
|
||||
|
||||
Pico.m.eeprom_status = (unsigned char) sreg;
|
||||
}
|
||||
|
||||
void EEPROM_write16(unsigned int d)
|
||||
{
|
||||
// this diff must be at most 16 for NBA Jam to work
|
||||
if (SekCyclesDone() - last_write < 16) {
|
||||
// just update pending state
|
||||
elprintf(EL_EEPROM, "eeprom: skip because cycles=%i",
|
||||
SekCyclesDone() - last_write);
|
||||
EEPROM_upd_pending(d);
|
||||
} else {
|
||||
int srs = Pico.m.eeprom_status;
|
||||
EEPROM_write_do(srs >> 6); // execute pending
|
||||
EEPROM_upd_pending(d);
|
||||
if ((srs ^ Pico.m.eeprom_status) & 0xc0) // update time only if SDA/SCL changed
|
||||
last_write = SekCyclesDone();
|
||||
}
|
||||
}
|
||||
|
||||
void EEPROM_write8(unsigned int a, unsigned int d)
|
||||
{
|
||||
unsigned char *wb = Pico.m.eeprom_wb;
|
||||
wb[a & 1] = d;
|
||||
EEPROM_write16((wb[0] << 8) | wb[1]);
|
||||
}
|
||||
|
||||
unsigned int EEPROM_read(void)
|
||||
{
|
||||
unsigned int shift, d;
|
||||
unsigned int sreg, saddr, scyc, ssa, interval;
|
||||
|
||||
// flush last pending write
|
||||
EEPROM_write_do(Pico.m.eeprom_status>>6);
|
||||
|
||||
sreg = Pico.m.eeprom_status; saddr = Pico.m.eeprom_addr&0x1fff; scyc = Pico.m.eeprom_cycle; ssa = Pico.m.eeprom_slave;
|
||||
interval = SekCyclesDone() - last_write;
|
||||
d = (sreg>>6)&1; // use SDA as "open bus"
|
||||
|
||||
// NBA Jam is nasty enough to read <before> raising the SCL and starting the new cycle.
|
||||
// this is probably valid because data changes occur while SCL is low and data can be read
|
||||
// before it's actual cycle begins.
|
||||
if (!(sreg&0x80) && interval >= 24) {
|
||||
elprintf(EL_EEPROM, "eeprom: early read, cycles=%i", interval);
|
||||
scyc++;
|
||||
}
|
||||
|
||||
if (!(sreg & 8)); // not started, use open bus
|
||||
else if (scyc == 9 || scyc == 18 || scyc == 27) {
|
||||
elprintf(EL_EEPROM, "eeprom: r ack");
|
||||
d = 0;
|
||||
} else if (scyc > 9 && scyc < 18) {
|
||||
// started and first command word received
|
||||
shift = 17-scyc;
|
||||
if (SRam.eeprom_type) {
|
||||
// X24C02+
|
||||
if (ssa&1) {
|
||||
elprintf(EL_EEPROM, "eeprom: read: addr %02x, cycle %i, reg %02x", saddr, scyc, sreg);
|
||||
if (shift==0) elprintf(EL_EEPROM, "eeprom: read done, byte %02x", SRam.data[saddr]);
|
||||
d = (SRam.data[saddr]>>shift)&1;
|
||||
}
|
||||
} else {
|
||||
// X24C01
|
||||
if (saddr&1) {
|
||||
elprintf(EL_EEPROM, "eeprom: read: addr %02x, cycle %i, reg %02x", saddr>>1, scyc, sreg);
|
||||
if (shift==0) elprintf(EL_EEPROM, "eeprom: read done, byte %02x", SRam.data[saddr>>1]);
|
||||
d = (SRam.data[saddr>>1]>>shift)&1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (d << SRam.eeprom_bit_out);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2006-2010,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "pico_int.h"
|
||||
#include "cd/cue.h"
|
||||
|
||||
unsigned char media_id_header[0x100];
|
||||
|
||||
static void strlwr_(char *string)
|
||||
{
|
||||
char *p;
|
||||
for (p = string; *p; p++)
|
||||
if ('A' <= *p && *p <= 'Z')
|
||||
*p += 'a' - 'A';
|
||||
}
|
||||
|
||||
static void get_ext(const char *file, char *ext)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = file + strlen(file) - 4;
|
||||
if (p < file)
|
||||
p = file;
|
||||
strncpy(ext, p, 4);
|
||||
ext[4] = 0;
|
||||
strlwr_(ext);
|
||||
}
|
||||
|
||||
enum media_type_e PicoLoadMedia(
|
||||
const char *filename,
|
||||
const char *carthw_cfg_fname,
|
||||
const char *(*get_bios_filename)(int *region, const char *cd_fname),
|
||||
void (*do_region_override)(const char *media_filename), enum media_type_e media_type)
|
||||
{
|
||||
const char *rom_fname = filename;
|
||||
enum cd_img_type cd_img_type = CIT_NOT_CD;
|
||||
unsigned char *rom_data = NULL;
|
||||
unsigned int rom_size = 0;
|
||||
pm_file *rom = NULL;
|
||||
int cd_region = 0;
|
||||
int ret;
|
||||
|
||||
if (media_type == PM_BAD_DETECT)
|
||||
goto out;
|
||||
|
||||
PicoAHW = 0;
|
||||
PicoQuirks = 0;
|
||||
|
||||
if (media_type == PM_CD)
|
||||
{
|
||||
// check for MegaCD image
|
||||
cd_img_type = PicoCdCheck(filename, &cd_region);
|
||||
if ((int)cd_img_type >= 0 && cd_img_type != CIT_NOT_CD)
|
||||
{
|
||||
// valid CD image, ask frontend for BIOS..
|
||||
rom_fname = NULL;
|
||||
if (get_bios_filename != NULL)
|
||||
rom_fname = get_bios_filename(&cd_region, filename);
|
||||
if (rom_fname == NULL)
|
||||
{
|
||||
media_type = PM_BAD_CD_NO_BIOS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
PicoAHW |= PAHW_MCD;
|
||||
}
|
||||
else
|
||||
{
|
||||
media_type = PM_BAD_CD;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (media_type == PM_MARK3)
|
||||
{
|
||||
lprintf("detected SMS ROM\n");
|
||||
PicoAHW = PAHW_SMS;
|
||||
}
|
||||
|
||||
rom = pm_open(rom_fname);
|
||||
if (rom == NULL)
|
||||
{
|
||||
lprintf("Failed to open ROM");
|
||||
media_type = PM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = PicoCartLoad(rom, &rom_data, &rom_size, (PicoAHW & PAHW_SMS) ? 1 : 0);
|
||||
pm_close(rom);
|
||||
if (ret != 0)
|
||||
{
|
||||
if (ret == 2)
|
||||
lprintf("Out of memory");
|
||||
else if (ret == 3)
|
||||
lprintf("Read failed");
|
||||
else
|
||||
lprintf("PicoCartLoad() failed.");
|
||||
media_type = PM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// detect wrong files
|
||||
if (strncmp((char *)rom_data, "Pico", 4) == 0)
|
||||
{
|
||||
lprintf("savestate selected?\n");
|
||||
media_type = PM_BAD_DETECT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(PicoAHW & PAHW_SMS))
|
||||
{
|
||||
unsigned short *d = (unsigned short *)(rom_data + 4);
|
||||
if ((((d[0] << 16) | d[1]) & 0xffffff) >= (int)rom_size)
|
||||
{
|
||||
lprintf("bad reset vector\n");
|
||||
media_type = PM_BAD_DETECT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// load config for this ROM (do this before insert to get correct region)
|
||||
if (!(PicoAHW & PAHW_MCD))
|
||||
{
|
||||
memcpy(media_id_header, rom_data + 0x100, sizeof(media_id_header));
|
||||
if (do_region_override != NULL)
|
||||
do_region_override(filename);
|
||||
}
|
||||
|
||||
if (PicoCartInsert(rom_data, rom_size, carthw_cfg_fname))
|
||||
{
|
||||
media_type = PM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
rom_data = NULL; // now belongs to PicoCart
|
||||
Pico.m.ncart_in = 0;
|
||||
|
||||
// insert CD if it was detected
|
||||
if (cd_img_type != CIT_NOT_CD)
|
||||
{
|
||||
ret = cdd_load(filename, cd_img_type);
|
||||
if (ret != 0)
|
||||
{
|
||||
media_type = PM_BAD_CD;
|
||||
goto out;
|
||||
}
|
||||
Pico.m.ncart_in = 1;
|
||||
}
|
||||
|
||||
if (PicoQuirks & PQUIRK_FORCE_6BTN)
|
||||
PicoSetInputDevice(0, PICO_INPUT_PAD_6BTN);
|
||||
|
||||
out:
|
||||
if (rom_data)
|
||||
free(rom_data);
|
||||
return media_type;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,140 @@
|
|||
// memory map related stuff
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long uptr; // unsigned pointer-sized int
|
||||
|
||||
#define M68K_MEM_SHIFT 16
|
||||
// minimum size we can map
|
||||
#define M68K_BANK_SIZE (1 << M68K_MEM_SHIFT)
|
||||
#define M68K_BANK_MASK (M68K_BANK_SIZE - 1)
|
||||
|
||||
extern uptr m68k_read8_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr m68k_read16_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr m68k_write8_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr m68k_write16_map[0x1000000 >> M68K_MEM_SHIFT];
|
||||
|
||||
extern uptr s68k_read8_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr s68k_read16_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr s68k_write8_map [0x1000000 >> M68K_MEM_SHIFT];
|
||||
extern uptr s68k_write16_map[0x1000000 >> M68K_MEM_SHIFT];
|
||||
|
||||
// top-level handlers that cores can use
|
||||
// (or alternatively build them into themselves)
|
||||
// XXX: unhandled: *16 and *32 might cross the bank boundaries
|
||||
typedef u32 (cpu68k_read_f)(u32 a);
|
||||
typedef void (cpu68k_write_f)(u32 a, u32 d);
|
||||
|
||||
extern u32 m68k_read8(u32 a);
|
||||
extern u32 m68k_read16(u32 a);
|
||||
extern void m68k_write8(u32 a, u8 d);
|
||||
extern void m68k_write16(u32 a, u16 d);
|
||||
|
||||
// z80
|
||||
#define Z80_MEM_SHIFT 13
|
||||
extern uptr z80_read_map [0x10000 >> Z80_MEM_SHIFT];
|
||||
extern uptr z80_write_map[0x10000 >> Z80_MEM_SHIFT];
|
||||
typedef unsigned char (z80_read_f)(unsigned short a);
|
||||
typedef void (z80_write_f)(unsigned int a, unsigned char data);
|
||||
|
||||
void z80_map_set(uptr *map, int start_addr, int end_addr,
|
||||
const void *func_or_mh, int is_func);
|
||||
void cpu68k_map_set(uptr *map, int start_addr, int end_addr,
|
||||
const void *func_or_mh, int is_func);
|
||||
void cpu68k_map_all_ram(int start_addr, int end_addr, void *ptr, int is_sub);
|
||||
void m68k_map_unmap(int start_addr, int end_addr);
|
||||
|
||||
#define MAP_FLAG ((uptr)1 << (sizeof(uptr) * 8 - 1))
|
||||
#define map_flag_set(x) ((x) & MAP_FLAG)
|
||||
|
||||
#define MAKE_68K_READ8(name, map) \
|
||||
u32 name(u32 a) \
|
||||
{ \
|
||||
uptr v; \
|
||||
a &= 0x00ffffff; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
if (map_flag_set(v)) \
|
||||
return ((cpu68k_read_f *)(v << 1))(a); \
|
||||
else \
|
||||
return *(u8 *)((v << 1) + (a ^ 1)); \
|
||||
}
|
||||
|
||||
#define MAKE_68K_READ16(name, map) \
|
||||
u32 name(u32 a) \
|
||||
{ \
|
||||
uptr v; \
|
||||
a &= 0x00fffffe; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
if (map_flag_set(v)) \
|
||||
return ((cpu68k_read_f *)(v << 1))(a); \
|
||||
else \
|
||||
return *(u16 *)((v << 1) + a); \
|
||||
}
|
||||
|
||||
#define MAKE_68K_READ32(name, map) \
|
||||
u32 name(u32 a) \
|
||||
{ \
|
||||
uptr v, vs; \
|
||||
u32 d; \
|
||||
a &= 0x00fffffe; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
vs = v << 1; \
|
||||
if (map_flag_set(v)) { \
|
||||
d = ((cpu68k_read_f *)vs)(a) << 16; \
|
||||
d |= ((cpu68k_read_f *)vs)(a + 2); \
|
||||
} \
|
||||
else { \
|
||||
u16 *m = (u16 *)(vs + a); \
|
||||
d = (m[0] << 16) | m[1]; \
|
||||
} \
|
||||
return d; \
|
||||
}
|
||||
|
||||
#define MAKE_68K_WRITE8(name, map) \
|
||||
void name(u32 a, u8 d) \
|
||||
{ \
|
||||
uptr v; \
|
||||
a &= 0x00ffffff; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
if (map_flag_set(v)) \
|
||||
((cpu68k_write_f *)(v << 1))(a, d); \
|
||||
else \
|
||||
*(u8 *)((v << 1) + (a ^ 1)) = d; \
|
||||
}
|
||||
|
||||
#define MAKE_68K_WRITE16(name, map) \
|
||||
void name(u32 a, u16 d) \
|
||||
{ \
|
||||
uptr v; \
|
||||
a &= 0x00fffffe; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
if (map_flag_set(v)) \
|
||||
((cpu68k_write_f *)(v << 1))(a, d); \
|
||||
else \
|
||||
*(u16 *)((v << 1) + a) = d; \
|
||||
}
|
||||
|
||||
#define MAKE_68K_WRITE32(name, map) \
|
||||
void name(u32 a, u32 d) \
|
||||
{ \
|
||||
uptr v, vs; \
|
||||
a &= 0x00fffffe; \
|
||||
v = map[a >> M68K_MEM_SHIFT]; \
|
||||
vs = v << 1; \
|
||||
if (map_flag_set(v)) { \
|
||||
((cpu68k_write_f *)vs)(a, d >> 16); \
|
||||
((cpu68k_write_f *)vs)(a + 2, d); \
|
||||
} \
|
||||
else { \
|
||||
u16 *m = (u16 *)(vs + a); \
|
||||
m[0] = d >> 16; \
|
||||
m[1] = d; \
|
||||
} \
|
||||
}
|
||||
|
||||
// 32x
|
||||
typedef struct {
|
||||
uptr addr; // stores (membase >> 1) or ((handler >> 1) | (1<<31))
|
||||
u32 mask;
|
||||
} sh2_memmap;
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* rarely used EEPROM code
|
||||
* (C) notaz, 2006-2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
|
||||
// H-counter table for hvcounter reads in 40col mode
|
||||
// based on Gens code
|
||||
const unsigned char hcounts_40[] = {
|
||||
0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x0a,0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0d,0x0d,
|
||||
0x0e,0x0e,0x0e,0x0f,0x0f,0x10,0x10,0x10,0x11,0x11,0x12,0x12,0x13,0x13,0x13,0x14,
|
||||
0x14,0x15,0x15,0x15,0x16,0x16,0x17,0x17,0x18,0x18,0x18,0x19,0x19,0x1a,0x1a,0x1b,
|
||||
0x1b,0x1b,0x1c,0x1c,0x1d,0x1d,0x1d,0x1e,0x1e,0x1f,0x1f,0x20,0x20,0x20,0x21,0x21,
|
||||
0x22,0x22,0x23,0x23,0x23,0x24,0x24,0x25,0x25,0x25,0x26,0x26,0x27,0x27,0x28,0x28,
|
||||
0x28,0x29,0x29,0x2a,0x2a,0x2a,0x2b,0x2b,0x2c,0x2c,0x2d,0x2d,0x2d,0x2e,0x2e,0x2f,
|
||||
0x2f,0x30,0x30,0x30,0x31,0x31,0x32,0x32,0x32,0x33,0x33,0x34,0x34,0x35,0x35,0x35,
|
||||
0x36,0x36,0x37,0x37,0x38,0x38,0x38,0x39,0x39,0x3a,0x3a,0x3a,0x3b,0x3b,0x3c,0x3c,
|
||||
0x3d,0x3d,0x3d,0x3e,0x3e,0x3f,0x3f,0x3f,0x40,0x40,0x41,0x41,0x42,0x42,0x42,0x43,
|
||||
0x43,0x44,0x44,0x45,0x45,0x45,0x46,0x46,0x47,0x47,0x47,0x48,0x48,0x49,0x49,0x4a,
|
||||
0x4a,0x4a,0x4b,0x4b,0x4c,0x4c,0x4d,0x4d,0x4d,0x4e,0x4e,0x4f,0x4f,0x4f,0x50,0x50,
|
||||
0x51,0x51,0x52,0x52,0x52,0x53,0x53,0x54,0x54,0x55,0x55,0x55,0x56,0x56,0x57,0x57,
|
||||
0x57,0x58,0x58,0x59,0x59,0x5a,0x5a,0x5a,0x5b,0x5b,0x5c,0x5c,0x5c,0x5d,0x5d,0x5e,
|
||||
0x5e,0x5f,0x5f,0x5f,0x60,0x60,0x61,0x61,0x62,0x62,0x62,0x63,0x63,0x64,0x64,0x64,
|
||||
0x65,0x65,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x69,0x69,0x6a,0x6a,0x6a,0x6b,0x6b,
|
||||
0x6c,0x6c,0x6c,0x6d,0x6d,0x6e,0x6e,0x6f,0x6f,0x6f,0x70,0x70,0x71,0x71,0x71,0x72,
|
||||
0x72,0x73,0x73,0x74,0x74,0x74,0x75,0x75,0x76,0x76,0x77,0x77,0x77,0x78,0x78,0x79,
|
||||
0x79,0x79,0x7a,0x7a,0x7b,0x7b,0x7c,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e,0x7f,0x7f,0x7f,
|
||||
0x80,0x80,0x81,0x81,0x81,0x82,0x82,0x83,0x83,0x84,0x84,0x84,0x85,0x85,0x86,0x86,
|
||||
0x86,0x87,0x87,0x88,0x88,0x89,0x89,0x89,0x8a,0x8a,0x8b,0x8b,0x8c,0x8c,0x8c,0x8d,
|
||||
0x8d,0x8e,0x8e,0x8e,0x8f,0x8f,0x90,0x90,0x91,0x91,0x91,0x92,0x92,0x93,0x93,0x94,
|
||||
0x94,0x94,0x95,0x95,0x96,0x96,0x96,0x97,0x97,0x98,0x98,0x99,0x99,0x99,0x9a,0x9a,
|
||||
0x9b,0x9b,0x9b,0x9c,0x9c,0x9d,0x9d,0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa1,0xa1,
|
||||
0xa1,0xa2,0xa2,0xa3,0xa3,0xa3,0xa4,0xa4,0xa5,0xa5,0xa6,0xa6,0xa6,0xa7,0xa7,0xa8,
|
||||
0xa8,0xa9,0xa9,0xa9,0xaa,0xaa,0xab,0xab,0xab,0xac,0xac,0xad,0xad,0xae,0xae,0xae,
|
||||
0xaf,0xaf,0xb0,0xb0,
|
||||
0xe4,0xe4,0xe4,0xe5,0xe5,0xe6,0xe6,0xe6,0xe7,0xe7,0xe8,0xe8,0xe9,0xe9,0xe9,0xea,
|
||||
0xea,0xeb,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,
|
||||
0xf1,0xf1,0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,
|
||||
0xf8,0xf8,0xf9,0xf9,0xf9,0xfa,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfe,0xfe,
|
||||
0xfe,0xff,0xff,0x00,0x00,0x00,0x01,0x01,0x02,0x02,0x03,0x03,0x03,0x04,0x04,0x05,
|
||||
0x05,0x06,0x06,0x06,
|
||||
0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x0a,0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0d,0x0d,
|
||||
0x0e,0x0e,0x0e,0x0f,0x0f,0x10,0x10,0x10,
|
||||
};
|
||||
|
||||
// H-counter table for hvcounter reads in 32col mode
|
||||
const unsigned char hcounts_32[] = {
|
||||
0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x09,0x0a,0x0a,
|
||||
0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x0e,0x0e,0x0f,0x0f,0x0f,0x10,
|
||||
0x10,0x10,0x11,0x11,0x11,0x12,0x12,0x12,0x13,0x13,0x13,0x14,0x14,0x14,0x15,0x15,
|
||||
0x15,0x16,0x16,0x17,0x17,0x17,0x18,0x18,0x18,0x19,0x19,0x19,0x1a,0x1a,0x1a,0x1b,
|
||||
0x1b,0x1b,0x1c,0x1c,0x1c,0x1d,0x1d,0x1d,0x1e,0x1e,0x1f,0x1f,0x1f,0x20,0x20,0x20,
|
||||
0x21,0x21,0x21,0x22,0x22,0x22,0x23,0x23,0x23,0x24,0x24,0x24,0x25,0x25,0x26,0x26,
|
||||
0x26,0x27,0x27,0x27,0x28,0x28,0x28,0x29,0x29,0x29,0x2a,0x2a,0x2a,0x2b,0x2b,0x2b,
|
||||
0x2c,0x2c,0x2c,0x2d,0x2d,0x2e,0x2e,0x2e,0x2f,0x2f,0x2f,0x30,0x30,0x30,0x31,0x31,
|
||||
0x31,0x32,0x32,0x32,0x33,0x33,0x33,0x34,0x34,0x34,0x35,0x35,0x36,0x36,0x36,0x37,
|
||||
0x37,0x37,0x38,0x38,0x38,0x39,0x39,0x39,0x3a,0x3a,0x3a,0x3b,0x3b,0x3b,0x3c,0x3c,
|
||||
0x3d,0x3d,0x3d,0x3e,0x3e,0x3e,0x3f,0x3f,0x3f,0x40,0x40,0x40,0x41,0x41,0x41,0x42,
|
||||
0x42,0x42,0x43,0x43,0x43,0x44,0x44,0x45,0x45,0x45,0x46,0x46,0x46,0x47,0x47,0x47,
|
||||
0x48,0x48,0x48,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x4b,0x4b,0x4b,0x4c,0x4c,0x4d,0x4d,
|
||||
0x4d,0x4e,0x4e,0x4e,0x4f,0x4f,0x4f,0x50,0x50,0x50,0x51,0x51,0x51,0x52,0x52,0x52,
|
||||
0x53,0x53,0x53,0x54,0x54,0x55,0x55,0x55,0x56,0x56,0x56,0x57,0x57,0x57,0x58,0x58,
|
||||
0x58,0x59,0x59,0x59,0x5a,0x5a,0x5a,0x5b,0x5b,0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5e,
|
||||
0x5e,0x5e,0x5f,0x5f,0x5f,0x60,0x60,0x60,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63,
|
||||
0x64,0x64,0x64,0x65,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69,
|
||||
0x69,0x69,0x6a,0x6a,0x6a,0x6b,0x6b,0x6c,0x6c,0x6c,0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,
|
||||
0x6f,0x6f,0x6f,0x70,0x70,0x70,0x71,0x71,0x71,0x72,0x72,0x72,0x73,0x73,0x74,0x74,
|
||||
0x74,0x75,0x75,0x75,0x76,0x76,0x76,0x77,0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x79,
|
||||
0x7a,0x7a,0x7b,0x7b,0x7b,0x7c,0x7c,0x7c,0x7d,0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f,
|
||||
0x7f,0x80,0x80,0x80,0x81,0x81,0x81,0x82,0x82,0x83,0x83,0x83,0x84,0x84,0x84,0x85,
|
||||
0x85,0x85,0x86,0x86,0x86,0x87,0x87,0x87,0x88,0x88,0x88,0x89,0x89,0x89,0x8a,0x8a,
|
||||
0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x90,
|
||||
0x90,0x90,0x91,0x91,
|
||||
0xe8,0xe8,0xe8,0xe9,0xe9,0xe9,0xea,0xea,0xea,0xeb,0xeb,0xeb,0xec,0xec,0xec,0xed,
|
||||
0xed,0xed,0xee,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf0,0xf1,0xf1,0xf1,0xf2,0xf2,0xf2,
|
||||
0xf3,0xf3,0xf3,0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf8,0xf8,
|
||||
0xf8,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,
|
||||
0xfe,0xfe,0xfe,0xff,0xff,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x02,0x03,0x03,
|
||||
0x03,0x04,0x04,0x04,
|
||||
0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x09,0x0a,0x0a,
|
||||
0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0c,0x0d,
|
||||
};
|
||||
|
||||
|
||||
#ifndef _ASM_MISC_C
|
||||
typedef struct
|
||||
{
|
||||
int b0;
|
||||
int b1;
|
||||
int b2;
|
||||
int b3;
|
||||
int b4;
|
||||
int b5;
|
||||
int b6;
|
||||
int b7;
|
||||
} intblock;
|
||||
|
||||
PICO_INTERNAL_ASM void memcpy16(unsigned short *dest, unsigned short *src, int count)
|
||||
{
|
||||
if ((((long)dest | (long)src) & 3) == 0)
|
||||
{
|
||||
if (count >= 32) {
|
||||
memcpy32((int *)dest, (int *)src, count/2);
|
||||
count&=1;
|
||||
} else {
|
||||
for (; count >= 2; count -= 2, dest+=2, src+=2)
|
||||
*(int *)dest = *(int *)src;
|
||||
}
|
||||
}
|
||||
while (count--)
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL_ASM void memcpy16bswap(unsigned short *dest, void *src, int count)
|
||||
{
|
||||
unsigned char *src_ = src;
|
||||
|
||||
for (; count; count--, src_ += 2)
|
||||
*dest++ = (src_[0] << 8) | src_[1];
|
||||
}
|
||||
|
||||
#ifndef _ASM_MISC_C_AMIPS
|
||||
PICO_INTERNAL_ASM void memcpy32(void *dest_in, const void *src_in, int count)
|
||||
{
|
||||
const intblock *bs = (intblock *) src_in;
|
||||
intblock *bd = (intblock *) dest_in;
|
||||
const int *src;
|
||||
int *dest;
|
||||
|
||||
for (; count >= sizeof(*bd)/4; count -= sizeof(*bd)/4)
|
||||
*bd++ = *bs++;
|
||||
|
||||
dest = (int *)bd; src = (const int *)bs;
|
||||
while (count--)
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL_ASM void memset32(void *dest_in, int c, int count)
|
||||
{
|
||||
int *dest = dest_in;
|
||||
|
||||
for (; count >= 8; count -= 8, dest += 8)
|
||||
dest[0] = dest[1] = dest[2] = dest[3] =
|
||||
dest[4] = dest[5] = dest[6] = dest[7] = c;
|
||||
|
||||
while (count--)
|
||||
*dest++ = c;
|
||||
}
|
||||
void memset32_uncached(int *dest, int c, int count) { memset32(dest, c, count); }
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* mode4/SMS renderer
|
||||
* (C) notaz, 2009-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
/*
|
||||
* TODO:
|
||||
* - TMS9918 modes?
|
||||
* - gg mode?
|
||||
* - column scroll (reg 0 bit7)
|
||||
* - 224/240 line modes
|
||||
* - doubled sprites
|
||||
*/
|
||||
#include "pico_int.h"
|
||||
|
||||
static void (*FinalizeLineM4)(int line);
|
||||
static int skip_next_line;
|
||||
static int screen_offset;
|
||||
|
||||
#define PLANAR_PIXEL(x,p) \
|
||||
t = pack & (0x80808080 >> p); \
|
||||
if (t) { \
|
||||
t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \
|
||||
pd[x] = pal|t; \
|
||||
}
|
||||
|
||||
static int TileNormM4(int sx, int addr, int pal)
|
||||
{
|
||||
unsigned char *pd = HighCol + sx;
|
||||
unsigned int pack, t;
|
||||
|
||||
pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
|
||||
if (pack)
|
||||
{
|
||||
PLANAR_PIXEL(0, 0)
|
||||
PLANAR_PIXEL(1, 1)
|
||||
PLANAR_PIXEL(2, 2)
|
||||
PLANAR_PIXEL(3, 3)
|
||||
PLANAR_PIXEL(4, 4)
|
||||
PLANAR_PIXEL(5, 5)
|
||||
PLANAR_PIXEL(6, 6)
|
||||
PLANAR_PIXEL(7, 7)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; /* Tile blank */
|
||||
}
|
||||
|
||||
static int TileFlipM4(int sx,int addr,int pal)
|
||||
{
|
||||
unsigned char *pd = HighCol + sx;
|
||||
unsigned int pack, t;
|
||||
|
||||
pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
|
||||
if (pack)
|
||||
{
|
||||
PLANAR_PIXEL(0, 7)
|
||||
PLANAR_PIXEL(1, 6)
|
||||
PLANAR_PIXEL(2, 5)
|
||||
PLANAR_PIXEL(3, 4)
|
||||
PLANAR_PIXEL(4, 3)
|
||||
PLANAR_PIXEL(5, 2)
|
||||
PLANAR_PIXEL(6, 1)
|
||||
PLANAR_PIXEL(7, 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; /* Tile blank */
|
||||
}
|
||||
|
||||
static void draw_sprites(int scanline)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
unsigned int sprites_addr[8];
|
||||
unsigned int sprites_x[8];
|
||||
unsigned char *sat;
|
||||
int xoff = 8; // relative to HighCol, which is (screen - 8)
|
||||
int sprite_base, addr_mask;
|
||||
int i, s, h;
|
||||
|
||||
if (pv->reg[0] & 8)
|
||||
xoff = 0;
|
||||
|
||||
sat = (unsigned char *)Pico.vram + ((pv->reg[5] & 0x7e) << 7);
|
||||
if (pv->reg[1] & 2) {
|
||||
addr_mask = 0xfe; h = 16;
|
||||
} else {
|
||||
addr_mask = 0xff; h = 8;
|
||||
}
|
||||
sprite_base = (pv->reg[6] & 4) << (13-2-1);
|
||||
|
||||
for (i = s = 0; i < 64 && s < 8; i++)
|
||||
{
|
||||
int y;
|
||||
y = sat[i] + 1;
|
||||
if (y == 0xd1)
|
||||
break;
|
||||
if (y + h <= scanline || scanline < y)
|
||||
continue; // not on this line
|
||||
|
||||
sprites_x[s] = xoff + sat[0x80 + i*2];
|
||||
sprites_addr[s] = sprite_base + ((sat[0x80 + i*2 + 1] & addr_mask) << (5-1)) +
|
||||
((scanline - y) << (2-1));
|
||||
s++;
|
||||
}
|
||||
|
||||
// now draw all sprites backwards
|
||||
for (--s; s >= 0; s--)
|
||||
TileNormM4(sprites_x[s], sprites_addr[s], 0x10);
|
||||
}
|
||||
|
||||
// tilex_ty_prio merged to reduce register pressure
|
||||
static void draw_strip(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio)
|
||||
{
|
||||
int oldcode = -1, blank = -1; // The tile we know is blank
|
||||
int addr = 0, pal = 0;
|
||||
|
||||
// Draw tiles across screen:
|
||||
for (; cells > 0; dx += 8, tilex_ty_prio++, cells--)
|
||||
{
|
||||
int code, zero;
|
||||
|
||||
code = nametab[tilex_ty_prio & 0x1f];
|
||||
if (code == blank)
|
||||
continue;
|
||||
if ((code ^ tilex_ty_prio) & 0x1000) // priority differs?
|
||||
continue;
|
||||
|
||||
if (code != oldcode) {
|
||||
oldcode = code;
|
||||
// Get tile address/2:
|
||||
addr = (code & 0x1ff) << 4;
|
||||
addr += tilex_ty_prio >> 16;
|
||||
if (code & 0x0400)
|
||||
addr ^= 0xe; // Y-flip
|
||||
|
||||
pal = (code>>7) & 0x10;
|
||||
}
|
||||
|
||||
if (code&0x0200) zero = TileFlipM4(dx, addr, pal);
|
||||
else zero = TileNormM4(dx, addr, pal);
|
||||
|
||||
if (zero)
|
||||
blank = code; // We know this tile is blank now
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawDisplayM4(int scanline)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
unsigned short *nametab;
|
||||
int line, tilex, dx, ty, cells;
|
||||
int cellskip = 0; // XXX
|
||||
int maxcells = 32;
|
||||
|
||||
// Find the line in the name table
|
||||
line = pv->reg[9] + scanline; // vscroll + scanline
|
||||
if (line >= 224)
|
||||
line -= 224;
|
||||
|
||||
// Find name table:
|
||||
nametab = Pico.vram;
|
||||
nametab += (pv->reg[2] & 0x0e) << (10-1);
|
||||
nametab += (line>>3) << (6-1);
|
||||
|
||||
dx = pv->reg[8]; // hscroll
|
||||
if (scanline < 16 && (pv->reg[0] & 0x40))
|
||||
dx = 0; // hscroll disabled for top 2 rows
|
||||
|
||||
tilex = ((-dx >> 3) + cellskip) & 0x1f;
|
||||
ty = (line & 7) << 1; // Y-Offset into tile
|
||||
cells = maxcells - cellskip;
|
||||
|
||||
dx = ((dx - 1) & 7) + 1;
|
||||
if (dx != 8)
|
||||
cells++; // have hscroll, need to draw 1 cell more
|
||||
dx += cellskip << 3;
|
||||
|
||||
// low priority tiles
|
||||
if (PicoDrawMask & PDRAW_LAYERB_ON)
|
||||
draw_strip(nametab, dx, cells, tilex | 0x0000 | (ty << 16));
|
||||
|
||||
// sprites
|
||||
if (PicoDrawMask & PDRAW_SPRITES_LOW_ON)
|
||||
draw_sprites(scanline);
|
||||
|
||||
// high priority tiles (use virtual layer switch just for fun)
|
||||
if (PicoDrawMask & PDRAW_LAYERA_ON)
|
||||
draw_strip(nametab, dx, cells, tilex | 0x1000 | (ty << 16));
|
||||
|
||||
if (pv->reg[0] & 0x20)
|
||||
// first column masked
|
||||
((int *)HighCol)[2] = ((int *)HighCol)[3] = 0xe0e0e0e0;
|
||||
}
|
||||
|
||||
void PicoFrameStartMode4(void)
|
||||
{
|
||||
int lines = 192;
|
||||
skip_next_line = 0;
|
||||
screen_offset = 24;
|
||||
rendstatus = PDRAW_32_COLS;
|
||||
|
||||
if ((Pico.video.reg[0] & 6) == 6 && (Pico.video.reg[1] & 0x18)) {
|
||||
if (Pico.video.reg[1] & 0x08) {
|
||||
screen_offset = 0;
|
||||
lines = 240;
|
||||
}
|
||||
else {
|
||||
screen_offset = 8;
|
||||
lines = 224;
|
||||
}
|
||||
}
|
||||
|
||||
if (rendstatus != rendstatus_old || lines != rendlines) {
|
||||
emu_video_mode_change(screen_offset, lines, 1);
|
||||
rendstatus_old = rendstatus;
|
||||
rendlines = lines;
|
||||
}
|
||||
|
||||
DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
|
||||
}
|
||||
|
||||
void PicoLineMode4(int line)
|
||||
{
|
||||
if (skip_next_line > 0) {
|
||||
skip_next_line--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (PicoScanBegin != NULL)
|
||||
skip_next_line = PicoScanBegin(line + screen_offset);
|
||||
|
||||
// Draw screen:
|
||||
BackFill(Pico.video.reg[7] & 0x0f, 0);
|
||||
if (Pico.video.reg[1] & 0x40)
|
||||
DrawDisplayM4(line);
|
||||
|
||||
if (FinalizeLineM4 != NULL)
|
||||
FinalizeLineM4(line);
|
||||
|
||||
if (PicoScanEnd != NULL)
|
||||
skip_next_line = PicoScanEnd(line + screen_offset);
|
||||
|
||||
DrawLineDest = (char *)DrawLineDest + DrawLineDestIncrement;
|
||||
}
|
||||
|
||||
void PicoDoHighPal555M4(void)
|
||||
{
|
||||
unsigned int *spal=(void *)Pico.cram;
|
||||
unsigned int *dpal=(void *)HighPal;
|
||||
unsigned int t;
|
||||
int i;
|
||||
|
||||
Pico.m.dirtyPal = 0;
|
||||
|
||||
/* cram is always stored as shorts, even though real hardware probably uses bytes */
|
||||
for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
|
||||
t = *spal;
|
||||
#ifdef USE_BGR555
|
||||
t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)<<10);
|
||||
#else
|
||||
t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1);
|
||||
#endif
|
||||
t |= t >> 2;
|
||||
t |= (t >> 4) & 0x08610861;
|
||||
*dpal = t;
|
||||
}
|
||||
HighPal[0xe0] = 0;
|
||||
}
|
||||
|
||||
static void FinalizeLineRGB555M4(int line)
|
||||
{
|
||||
if (Pico.m.dirtyPal)
|
||||
PicoDoHighPal555M4();
|
||||
|
||||
// standard FinalizeLine can finish it for us,
|
||||
// with features like scaling and such
|
||||
FinalizeLine555(0, line);
|
||||
}
|
||||
|
||||
static void FinalizeLine8bitM4(int line)
|
||||
{
|
||||
unsigned char *pd = DrawLineDest;
|
||||
|
||||
if (!(PicoOpt & POPT_DIS_32C_BORDER))
|
||||
pd += 32;
|
||||
|
||||
memcpy32((int *)pd, (int *)(HighCol+8), 256/4);
|
||||
}
|
||||
|
||||
void PicoDrawSetOutputMode4(pdso_t which)
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case PDF_8BIT: FinalizeLineM4 = FinalizeLine8bitM4; break;
|
||||
case PDF_RGB555: FinalizeLineM4 = FinalizeLineRGB555M4; break;
|
||||
default: FinalizeLineM4 = NULL; break;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
#include "sound/ym2612.h"
|
||||
|
||||
struct Pico Pico;
|
||||
int PicoOpt;
|
||||
int PicoSkipFrame; // skip rendering frame?
|
||||
int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU
|
||||
int PicoPadInt[2]; // internal copy
|
||||
int PicoAHW; // active addon hardware: PAHW_*
|
||||
int PicoQuirks; // game-specific quirks
|
||||
int PicoRegionOverride; // override the region detection 0: Auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe
|
||||
int PicoAutoRgnOrder;
|
||||
|
||||
struct PicoSRAM SRam;
|
||||
int emustatus; // rapid_ym2612, multi_ym_updates
|
||||
int scanlines_total;
|
||||
|
||||
void (*PicoWriteSound)(int len) = NULL; // called at the best time to send sound buffer (PsndOut) to hardware
|
||||
void (*PicoResetHook)(void) = NULL;
|
||||
void (*PicoLineHook)(void) = NULL;
|
||||
|
||||
// to be called once on emu init
|
||||
void PicoInit(void)
|
||||
{
|
||||
// Blank space for state:
|
||||
memset(&Pico,0,sizeof(Pico));
|
||||
memset(&PicoPad,0,sizeof(PicoPad));
|
||||
memset(&PicoPadInt,0,sizeof(PicoPadInt));
|
||||
|
||||
// Init CPUs:
|
||||
SekInit();
|
||||
z80_init(); // init even if we aren't going to use it
|
||||
|
||||
PicoInitMCD();
|
||||
PicoSVPInit();
|
||||
Pico32xInit();
|
||||
}
|
||||
|
||||
void PicoPower(void)
|
||||
{
|
||||
Pico.m.frame_count = 0;
|
||||
SekCycleCnt = SekCycleAim = 0;
|
||||
|
||||
// clear all memory of the emulated machine
|
||||
memset(&Pico.ram,0,(unsigned char *)&Pico.rom - Pico.ram);
|
||||
|
||||
memset(&Pico.video,0,sizeof(Pico.video));
|
||||
memset(&Pico.m,0,sizeof(Pico.m));
|
||||
|
||||
Pico.video.pending_ints=0;
|
||||
z80_reset();
|
||||
|
||||
// my MD1 VA6 console has this in IO
|
||||
Pico.ioports[1] = Pico.ioports[2] = Pico.ioports[3] = 0xff;
|
||||
|
||||
// default VDP register values (based on Fusion)
|
||||
Pico.video.reg[0] = Pico.video.reg[1] = 0x04;
|
||||
Pico.video.reg[0xc] = 0x81;
|
||||
Pico.video.reg[0xf] = 0x02;
|
||||
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
PicoPowerMCD();
|
||||
|
||||
if (PicoOpt & POPT_EN_32X)
|
||||
PicoPower32x();
|
||||
|
||||
PicoReset();
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoDetectRegion(void)
|
||||
{
|
||||
int support=0, hw=0, i;
|
||||
unsigned char pal=0;
|
||||
|
||||
if (PicoRegionOverride)
|
||||
{
|
||||
support = PicoRegionOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read cartridge region data:
|
||||
unsigned short *rd = (unsigned short *)(Pico.rom + 0x1f0);
|
||||
int region = (rd[0] << 16) | rd[1];
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = region >> (i<<3);
|
||||
c &= 0xff;
|
||||
if (c <= ' ') continue;
|
||||
|
||||
if (c=='J') support|=1;
|
||||
else if (c=='U') support|=4;
|
||||
else if (c=='E') support|=8;
|
||||
else if (c=='j') {support|=1; break; }
|
||||
else if (c=='u') {support|=4; break; }
|
||||
else if (c=='e') {support|=8; break; }
|
||||
else
|
||||
{
|
||||
// New style code:
|
||||
char s[2]={0,0};
|
||||
s[0]=(char)c;
|
||||
support|=strtol(s,NULL,16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// auto detection order override
|
||||
if (PicoAutoRgnOrder) {
|
||||
if (((PicoAutoRgnOrder>>0)&0xf) & support) support = (PicoAutoRgnOrder>>0)&0xf;
|
||||
else if (((PicoAutoRgnOrder>>4)&0xf) & support) support = (PicoAutoRgnOrder>>4)&0xf;
|
||||
else if (((PicoAutoRgnOrder>>8)&0xf) & support) support = (PicoAutoRgnOrder>>8)&0xf;
|
||||
}
|
||||
|
||||
// Try to pick the best hardware value for English/50hz:
|
||||
if (support&8) { hw=0xc0; pal=1; } // Europe
|
||||
else if (support&4) hw=0x80; // USA
|
||||
else if (support&2) { hw=0x40; pal=1; } // Japan PAL
|
||||
else if (support&1) hw=0x00; // Japan NTSC
|
||||
else hw=0x80; // USA
|
||||
|
||||
Pico.m.hardware=(unsigned char)(hw|0x20); // No disk attached
|
||||
Pico.m.pal=pal;
|
||||
}
|
||||
|
||||
int PicoReset(void)
|
||||
{
|
||||
if (Pico.romsize <= 0)
|
||||
return 1;
|
||||
|
||||
#if defined(CPU_CMP_R) || defined(CPU_CMP_W) || defined(DRC_CMP)
|
||||
PicoOpt |= POPT_DIS_VDP_FIFO|POPT_DIS_IDLE_DET;
|
||||
#endif
|
||||
|
||||
/* must call now, so that banking is reset, and correct vectors get fetched */
|
||||
if (PicoResetHook)
|
||||
PicoResetHook();
|
||||
|
||||
memset(&PicoPadInt,0,sizeof(PicoPadInt));
|
||||
emustatus = 0;
|
||||
|
||||
if (PicoAHW & PAHW_SMS) {
|
||||
PicoResetMS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SekReset();
|
||||
// ..but do not reset SekCycle* to not desync with addons
|
||||
|
||||
// s68k doesn't have the TAS quirk, so we just globally set normal TAS handler in MCD mode (used by Batman games).
|
||||
SekSetRealTAS(PicoAHW & PAHW_MCD);
|
||||
|
||||
Pico.m.dirtyPal = 1;
|
||||
|
||||
Pico.m.z80_bank68k = 0;
|
||||
Pico.m.z80_reset = 1;
|
||||
|
||||
PicoDetectRegion();
|
||||
Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal
|
||||
|
||||
PsndReset(); // pal must be known here
|
||||
|
||||
// create an empty "dma" to cause 68k exec start at random frame location
|
||||
if (Pico.m.dma_xfers == 0 && !(PicoOpt & POPT_DIS_VDP_FIFO))
|
||||
Pico.m.dma_xfers = rand() & 0x1fff;
|
||||
|
||||
SekFinishIdleDet();
|
||||
|
||||
if (PicoAHW & PAHW_MCD) {
|
||||
PicoResetMCD();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reinit, so that checksum checks pass
|
||||
if (!(PicoOpt & POPT_DIS_IDLE_DET))
|
||||
SekInitIdleDet();
|
||||
|
||||
if (PicoOpt & POPT_EN_32X)
|
||||
PicoReset32x();
|
||||
|
||||
// reset sram state; enable sram access by default if it doesn't overlap with ROM
|
||||
Pico.m.sram_reg = 0;
|
||||
if ((SRam.flags & SRF_EEPROM) || Pico.romsize <= SRam.start)
|
||||
Pico.m.sram_reg |= SRR_MAPPED;
|
||||
|
||||
if (SRam.flags & SRF_ENABLED)
|
||||
elprintf(EL_STATUS, "sram: %06x - %06x; eeprom: %i", SRam.start, SRam.end,
|
||||
!!(SRam.flags & SRF_EEPROM));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// flush config changes before emu loop starts
|
||||
void PicoLoopPrepare(void)
|
||||
{
|
||||
if (PicoRegionOverride)
|
||||
// force setting possibly changed..
|
||||
Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;
|
||||
|
||||
// FIXME: PAL has 313 scanlines..
|
||||
scanlines_total = Pico.m.pal ? 312 : 262;
|
||||
|
||||
Pico.m.dirtyPal = 1;
|
||||
rendstatus_old = -1;
|
||||
}
|
||||
|
||||
|
||||
// dma2vram settings are just hacks to unglitch Legend of Galahad (needs <= 104 to work)
|
||||
// same for Outrunners (92-121, when active is set to 24)
|
||||
// 96 is VR hack
|
||||
static const int dma_timings[] = {
|
||||
167, 167, 166, 83, // vblank: 32cell: dma2vram dma2[vs|c]ram vram_fill vram_copy
|
||||
102, 205, 204, 102, // vblank: 40cell:
|
||||
16, 16, 15, 8, // active: 32cell:
|
||||
24, 18, 17, 9 // ...
|
||||
};
|
||||
|
||||
static const int dma_bsycles[] = {
|
||||
(488<<8)/167, (488<<8)/167, (488<<8)/166, (488<<8)/83,
|
||||
(488<<8)/102, (488<<8)/233, (488<<8)/204, (488<<8)/102,
|
||||
(488<<8)/16, (488<<8)/16, (488<<8)/15, (488<<8)/8,
|
||||
(488<<8)/24, (488<<8)/18, (488<<8)/17, (488<<8)/9
|
||||
};
|
||||
|
||||
// grossly inaccurate.. FIXME FIXXXMEE
|
||||
PICO_INTERNAL int CheckDMA(void)
|
||||
{
|
||||
int burn = 0, xfers_can, dma_op = Pico.video.reg[0x17]>>6; // see gens for 00 and 01 modes
|
||||
int xfers = Pico.m.dma_xfers;
|
||||
int dma_op1;
|
||||
|
||||
if(!(dma_op&2)) dma_op = (Pico.video.type==1) ? 0 : 1; // setting dma_timings offset here according to Gens
|
||||
dma_op1 = dma_op;
|
||||
if(Pico.video.reg[12] & 1) dma_op |= 4; // 40 cell mode?
|
||||
if(!(Pico.video.status&8)&&(Pico.video.reg[1]&0x40)) dma_op|=8; // active display?
|
||||
xfers_can = dma_timings[dma_op];
|
||||
if(xfers <= xfers_can)
|
||||
{
|
||||
if(dma_op&2) Pico.video.status&=~2; // dma no longer busy
|
||||
else {
|
||||
burn = xfers * dma_bsycles[dma_op] >> 8; // have to be approximate because can't afford division..
|
||||
}
|
||||
Pico.m.dma_xfers = 0;
|
||||
} else {
|
||||
if(!(dma_op&2)) burn = 488;
|
||||
Pico.m.dma_xfers -= xfers_can;
|
||||
}
|
||||
|
||||
elprintf(EL_VDPDMA, "~Dma %i op=%i can=%i burn=%i [%i]", Pico.m.dma_xfers, dma_op1, xfers_can, burn, SekCyclesDone());
|
||||
//dprintf("~aim: %i, cnt: %i", SekCycleAim, SekCycleCnt);
|
||||
return burn;
|
||||
}
|
||||
|
||||
#include "pico_cmn.inc"
|
||||
|
||||
unsigned int last_z80_sync; /* in 68k cycles */
|
||||
int z80_cycle_cnt;
|
||||
int z80_cycle_aim;
|
||||
int z80_scanline;
|
||||
int z80_scanline_cycles; /* cycles done until z80_scanline */
|
||||
|
||||
/* sync z80 to 68k */
|
||||
PICO_INTERNAL void PicoSyncZ80(unsigned int m68k_cycles_done)
|
||||
{
|
||||
int m68k_cnt;
|
||||
int cnt;
|
||||
|
||||
m68k_cnt = m68k_cycles_done - last_z80_sync;
|
||||
z80_cycle_aim += cycles_68k_to_z80(m68k_cnt);
|
||||
cnt = z80_cycle_aim - z80_cycle_cnt;
|
||||
last_z80_sync = m68k_cycles_done;
|
||||
|
||||
pprof_start(z80);
|
||||
|
||||
elprintf(EL_BUSREQ, "z80 sync %i (%u|%u -> %u|%u)", cnt,
|
||||
z80_cycle_cnt, z80_cycle_cnt / 288,
|
||||
z80_cycle_aim, z80_cycle_aim / 288);
|
||||
|
||||
if (cnt > 0)
|
||||
z80_cycle_cnt += z80_run(cnt);
|
||||
|
||||
pprof_end(z80);
|
||||
}
|
||||
|
||||
|
||||
void PicoFrame(void)
|
||||
{
|
||||
pprof_start(frame);
|
||||
|
||||
Pico.m.frame_count++;
|
||||
|
||||
if (PicoAHW & PAHW_SMS) {
|
||||
PicoFrameMS();
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (PicoAHW & PAHW_32X) {
|
||||
PicoFrame32x(); // also does MCD+32X
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (PicoAHW & PAHW_MCD) {
|
||||
PicoFrameMCD();
|
||||
goto end;
|
||||
}
|
||||
|
||||
//if(Pico.video.reg[12]&0x2) Pico.video.status ^= 0x10; // change odd bit in interlace mode
|
||||
|
||||
PicoFrameStart();
|
||||
PicoFrameHints();
|
||||
|
||||
end:
|
||||
pprof_end(frame);
|
||||
}
|
||||
|
||||
void PicoFrameDrawOnly(void)
|
||||
{
|
||||
if (!(PicoAHW & PAHW_SMS)) {
|
||||
PicoFrameStart();
|
||||
PicoDrawSync(223, 0);
|
||||
} else {
|
||||
PicoFrameDrawOnlyMS();
|
||||
}
|
||||
}
|
||||
|
||||
void PicoGetInternal(pint_t which, pint_ret_t *r)
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case PI_ROM: r->vptr = Pico.rom; break;
|
||||
case PI_ISPAL: r->vint = Pico.m.pal; break;
|
||||
case PI_IS40_CELL: r->vint = Pico.video.reg[12]&1; break;
|
||||
case PI_IS240_LINES: r->vint = Pico.m.pal && (Pico.video.reg[1]&8); break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PICO_H
|
||||
#define PICO_H
|
||||
|
||||
#include <stdlib.h> // size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// message log
|
||||
extern void lprintf(const char *fmt, ...);
|
||||
|
||||
// external funcs for Sega/Mega CD
|
||||
extern int mp3_get_bitrate(void *f, int size);
|
||||
extern void mp3_start_play(void *f, int pos);
|
||||
extern void mp3_update(int *buffer, int length, int stereo);
|
||||
|
||||
// this one should handle display mode changes
|
||||
extern void emu_video_mode_change(int start_line, int line_count, int is_32cols);
|
||||
|
||||
// this must switch to 16bpp mode
|
||||
extern void emu_32x_startup(void);
|
||||
|
||||
// optional 32X BIOS, should be left NULL if not used
|
||||
// must be 256, 2048, 1024 bytes
|
||||
extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
|
||||
|
||||
// Pico.c
|
||||
#define POPT_EN_FM (1<< 0) // 00 000x
|
||||
#define POPT_EN_PSG (1<< 1)
|
||||
#define POPT_EN_Z80 (1<< 2)
|
||||
#define POPT_EN_STEREO (1<< 3)
|
||||
#define POPT_ALT_RENDERER (1<< 4) // 00 00x0
|
||||
// unused (1<< 5)
|
||||
// unused (1<< 6)
|
||||
#define POPT_ACC_SPRITES (1<< 7)
|
||||
#define POPT_DIS_32C_BORDER (1<< 8) // 00 0x00
|
||||
#define POPT_EXT_FM (1<< 9)
|
||||
#define POPT_EN_MCD_PCM (1<<10)
|
||||
#define POPT_EN_MCD_CDDA (1<<11)
|
||||
#define POPT_EN_MCD_GFX (1<<12) // 00 x000
|
||||
// unused (1<<13)
|
||||
#define POPT_EN_SOFTSCALE (1<<14)
|
||||
#define POPT_EN_MCD_RAMCART (1<<15)
|
||||
#define POPT_DIS_VDP_FIFO (1<<16) // 0x 0000
|
||||
#define POPT_EN_DRC (1<<17)
|
||||
#define POPT_DIS_SPRITE_LIM (1<<18)
|
||||
#define POPT_DIS_IDLE_DET (1<<19)
|
||||
#define POPT_EN_32X (1<<20)
|
||||
#define POPT_EN_PWM (1<<21)
|
||||
extern int PicoOpt; // bitfield
|
||||
|
||||
#define PAHW_MCD (1<<0)
|
||||
#define PAHW_32X (1<<1)
|
||||
#define PAHW_SVP (1<<2)
|
||||
#define PAHW_PICO (1<<3)
|
||||
#define PAHW_SMS (1<<4)
|
||||
extern int PicoAHW; // Pico active hw
|
||||
|
||||
#define PQUIRK_FORCE_6BTN (1<<0)
|
||||
extern int PicoQuirks;
|
||||
|
||||
extern int PicoSkipFrame; // skip rendering frame, but still do sound (if enabled) and emulation stuff
|
||||
extern int PicoRegionOverride; // override the region detection 0: auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe
|
||||
extern int PicoAutoRgnOrder; // packed priority list of regions, for example 0x148 means this detection order: EUR, USA, JAP
|
||||
extern int PicoSVPCycles;
|
||||
void PicoInit(void);
|
||||
void PicoPower(void);
|
||||
int PicoReset(void);
|
||||
void PicoLoopPrepare(void);
|
||||
void PicoFrame(void);
|
||||
void PicoFrameDrawOnly(void);
|
||||
extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU
|
||||
extern void (*PicoWriteSound)(int bytes); // called once per frame at the best time to send sound buffer (PsndOut) to hardware
|
||||
typedef enum { PI_ROM, PI_ISPAL, PI_IS40_CELL, PI_IS240_LINES } pint_t;
|
||||
typedef union { int vint; void *vptr; } pint_ret_t;
|
||||
void PicoGetInternal(pint_t which, pint_ret_t *ret);
|
||||
|
||||
// cd/mcd.c
|
||||
extern void (*PicoMCDopenTray)(void);
|
||||
extern void (*PicoMCDcloseTray)(void);
|
||||
|
||||
// pico.c
|
||||
#define XPCM_BUFFER_SIZE (320+160)
|
||||
typedef struct
|
||||
{
|
||||
int pen_pos[2];
|
||||
int page;
|
||||
// internal
|
||||
int fifo_bytes; // bytes in FIFO
|
||||
int fifo_bytes_prev;
|
||||
int fifo_line_bytes; // float part, << 16
|
||||
int line_counter;
|
||||
unsigned short r1, r12;
|
||||
unsigned char xpcm_buffer[XPCM_BUFFER_SIZE+4];
|
||||
unsigned char *xpcm_ptr;
|
||||
} picohw_state;
|
||||
extern picohw_state PicoPicohw;
|
||||
|
||||
// cd/cdd.c
|
||||
int cdd_load(const char *filename, int type);
|
||||
|
||||
// Cart.c
|
||||
typedef enum
|
||||
{
|
||||
PMT_UNCOMPRESSED = 0,
|
||||
PMT_ZIP,
|
||||
PMT_CSO
|
||||
} pm_type;
|
||||
typedef struct
|
||||
{
|
||||
void *file; /* file handle */
|
||||
void *param; /* additional file related field */
|
||||
unsigned int size; /* size */
|
||||
pm_type type;
|
||||
char ext[4];
|
||||
} pm_file;
|
||||
pm_file *pm_open(const char *path);
|
||||
size_t pm_read(void *ptr, size_t bytes, pm_file *stream);
|
||||
int pm_seek(pm_file *stream, long offset, int whence);
|
||||
int pm_close(pm_file *fp);
|
||||
int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize,int is_sms);
|
||||
int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg);
|
||||
extern void (*PicoCDLoadProgressCB)(const char *fname, int percent);
|
||||
extern int PicoGameLoaded;
|
||||
|
||||
// Draw.c
|
||||
// for line-based renderer, set conversion
|
||||
// from internal 8 bit representation in 'HighCol' to:
|
||||
typedef enum
|
||||
{
|
||||
PDF_NONE = 0, // no conversion
|
||||
PDF_RGB555, // RGB/BGR output, depends on compile options
|
||||
PDF_8BIT, // 8-bit out (handles shadow/hilight mode, sonic water)
|
||||
} pdso_t;
|
||||
void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode);
|
||||
void PicoDrawSetOutBuf(void *dest, int increment);
|
||||
void PicoDrawSetCallbacks(int (*begin)(unsigned int num), int (*end)(unsigned int num));
|
||||
extern void *DrawLineDest;
|
||||
extern unsigned char *HighCol;
|
||||
// utility
|
||||
#ifdef _ASM_DRAW_C
|
||||
void vidConvCpyRGB565(void *to, void *from, int pixels);
|
||||
#endif
|
||||
void PicoDoHighPal555(int sh);
|
||||
extern int PicoDrawMask;
|
||||
#define PDRAW_LAYERB_ON (1<<2)
|
||||
#define PDRAW_LAYERA_ON (1<<3)
|
||||
#define PDRAW_SPRITES_LOW_ON (1<<4)
|
||||
#define PDRAW_SPRITES_HI_ON (1<<7)
|
||||
#define PDRAW_32X_ON (1<<8)
|
||||
// internals
|
||||
#define PDRAW_SPRITES_MOVED (1<<0) // (asm)
|
||||
#define PDRAW_WND_DIFF_PRIO (1<<1) // not all window tiles use same priority
|
||||
#define PDRAW_SPR_LO_ON_HI (1<<2) // seen sprites without layer pri bit ontop spr. with that bit
|
||||
#define PDRAW_INTERLACE (1<<3)
|
||||
#define PDRAW_DIRTY_SPRITES (1<<4) // (asm)
|
||||
#define PDRAW_SONIC_MODE (1<<5) // mid-frame palette changes for 8bit renderer
|
||||
#define PDRAW_PLANE_HI_PRIO (1<<6) // have layer with all hi prio tiles (mk3)
|
||||
#define PDRAW_SHHI_DONE (1<<7) // layer sh/hi already processed
|
||||
#define PDRAW_32_COLS (1<<8) // 32 column mode
|
||||
extern int rendstatus, rendstatus_old;
|
||||
extern int rendlines;
|
||||
extern unsigned short HighPal[0x100];
|
||||
|
||||
// draw.c
|
||||
void PicoDrawUpdateHighPal(void);
|
||||
void PicoDrawSetInternalBuf(void *dest, int line_increment);
|
||||
|
||||
// draw2.c
|
||||
// stuff below is optional
|
||||
extern unsigned char *PicoDraw2FB; // buffer for fast renderer in format (8+320)x(8+224+8) (eights for borders)
|
||||
extern unsigned short *PicoCramHigh; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now)
|
||||
extern void (*PicoPrepareCram)(); // prepares PicoCramHigh for renderer to use
|
||||
|
||||
// pico.c (32x)
|
||||
#ifndef NO_32X
|
||||
|
||||
void Pico32xSetClocks(int msh2_hz, int ssh2_hz);
|
||||
|
||||
#else
|
||||
|
||||
#define Pico32xSetClocks(msh2_khz, ssh2_khz)
|
||||
|
||||
#endif
|
||||
|
||||
// normally 68k clock (7670442) * 3, in reality but much lower
|
||||
// because of high memory latencies
|
||||
#define PICO_MSH2_HZ ((int)(7670442.0 * 2.4))
|
||||
#define PICO_SSH2_HZ ((int)(7670442.0 * 2.4))
|
||||
|
||||
// sound.c
|
||||
extern int PsndRate,PsndLen;
|
||||
extern short *PsndOut;
|
||||
extern void (*PsndMix_32_to_16l)(short *dest, int *src, int count);
|
||||
void PsndRerate(int preserve_state);
|
||||
|
||||
// media.c
|
||||
enum media_type_e {
|
||||
PM_BAD_DETECT = -1,
|
||||
PM_ERROR = -2,
|
||||
PM_BAD_CD = -3,
|
||||
PM_BAD_CD_NO_BIOS = -4,
|
||||
PM_MD_CART = 1, /* also 32x */
|
||||
PM_MARK3,
|
||||
PM_CD,
|
||||
};
|
||||
|
||||
enum cd_img_type
|
||||
{
|
||||
CIT_NOT_CD = 0,
|
||||
CIT_ISO,
|
||||
CIT_BIN,
|
||||
CIT_CUE
|
||||
};
|
||||
|
||||
enum media_type_e PicoLoadMedia(const char *filename,
|
||||
const char *carthw_cfg_fname,
|
||||
const char *(*get_bios_filename)(int *region, const char *cd_fname),
|
||||
void (*do_region_override)(const char *media_filename), enum media_type_e media_type);
|
||||
int PicoCdCheck(const char *fname_in, int *pregion);
|
||||
|
||||
extern unsigned char media_id_header[0x100];
|
||||
|
||||
// memory.c
|
||||
enum input_device {
|
||||
PICO_INPUT_NOTHING,
|
||||
PICO_INPUT_PAD_3BTN,
|
||||
PICO_INPUT_PAD_6BTN,
|
||||
};
|
||||
void PicoSetInputDevice(int port, enum input_device device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // End of extern "C"
|
||||
#endif
|
||||
|
||||
#endif // PICO_H
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "../pico_int.h"
|
||||
#include "../memory.h"
|
||||
#include "../sound/sn76496.h"
|
||||
|
||||
/*
|
||||
void dump(u16 w)
|
||||
{
|
||||
static FILE *f[0x10] = { NULL, };
|
||||
char fname[32];
|
||||
int num = PicoPicohw.r12 & 0xf;
|
||||
|
||||
w = (w << 8) | (w >> 8);
|
||||
sprintf(fname, "ldump%i.bin", num);
|
||||
if (f[num] == NULL)
|
||||
f[num] = fopen(fname, "wb");
|
||||
fwrite(&w, 1, 2, f[num]);
|
||||
//fclose(f);
|
||||
}
|
||||
*/
|
||||
|
||||
static u32 PicoRead8_pico(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
if ((a & 0xffffe0) == 0x800000) // Pico I/O
|
||||
{
|
||||
switch (a & 0x1f)
|
||||
{
|
||||
case 0x01: d = PicoPicohw.r1; break;
|
||||
case 0x03:
|
||||
d = PicoPad[0]&0x1f; // d-pad
|
||||
d |= (PicoPad[0]&0x20) << 2; // pen push -> C
|
||||
d = ~d;
|
||||
break;
|
||||
|
||||
case 0x05: d = (PicoPicohw.pen_pos[0] >> 8); break; // what is MS bit for? Games read it..
|
||||
case 0x07: d = PicoPicohw.pen_pos[0] & 0xff; break;
|
||||
case 0x09: d = (PicoPicohw.pen_pos[1] >> 8); break;
|
||||
case 0x0b: d = PicoPicohw.pen_pos[1] & 0xff; break;
|
||||
case 0x0d: d = (1 << (PicoPicohw.page & 7)) - 1; break;
|
||||
case 0x12: d = PicoPicohw.fifo_bytes == 0 ? 0x80 : 0; break; // guess
|
||||
default:
|
||||
goto unhandled;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
unhandled:
|
||||
elprintf(EL_UIO, "m68k unmapped r8 [%06x] @%06x", a, SekPc);
|
||||
return d;
|
||||
}
|
||||
|
||||
static u32 PicoRead16_pico(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
if (a == 0x800010)
|
||||
d = (PicoPicohw.fifo_bytes > 0x3f) ? 0 : (0x3f - PicoPicohw.fifo_bytes);
|
||||
else if (a == 0x800012)
|
||||
d = PicoPicohw.fifo_bytes == 0 ? 0x8000 : 0; // guess
|
||||
else
|
||||
elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static void PicoWrite8_pico(u32 a, u32 d)
|
||||
{
|
||||
switch (a & ~0x800000) {
|
||||
case 0x19: case 0x1b: case 0x1d: case 0x1f: break; // 'S' 'E' 'G' 'A'
|
||||
default:
|
||||
elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void PicoWrite16_pico(u32 a, u32 d)
|
||||
{
|
||||
//if (a == 0x800010) dump(d);
|
||||
if (a == 0x800010)
|
||||
{
|
||||
PicoPicohw.fifo_bytes += 2;
|
||||
|
||||
if (PicoPicohw.xpcm_ptr < PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
|
||||
*PicoPicohw.xpcm_ptr++ = d >> 8;
|
||||
*PicoPicohw.xpcm_ptr++ = d;
|
||||
}
|
||||
else if (PicoPicohw.xpcm_ptr == PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
|
||||
elprintf(EL_ANOMALY|EL_PICOHW, "xpcm_buffer overflow!");
|
||||
PicoPicohw.xpcm_ptr++;
|
||||
}
|
||||
}
|
||||
else if (a == 0x800012) {
|
||||
int r12_old = PicoPicohw.r12;
|
||||
PicoPicohw.r12 = d;
|
||||
if (r12_old != d)
|
||||
PicoReratePico();
|
||||
}
|
||||
else
|
||||
elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PicoMemSetupPico(void)
|
||||
{
|
||||
PicoMemSetup();
|
||||
|
||||
// no MD IO or Z80 on Pico
|
||||
m68k_map_unmap(0x400000, 0xbfffff);
|
||||
|
||||
// map Pico I/O
|
||||
cpu68k_map_set(m68k_read8_map, 0x800000, 0x80ffff, PicoRead8_pico, 1);
|
||||
cpu68k_map_set(m68k_read16_map, 0x800000, 0x80ffff, PicoRead16_pico, 1);
|
||||
cpu68k_map_set(m68k_write8_map, 0x800000, 0x80ffff, PicoWrite8_pico, 1);
|
||||
cpu68k_map_set(m68k_write16_map, 0x800000, 0x80ffff, PicoWrite16_pico, 1);
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "../pico_int.h"
|
||||
|
||||
// x: 0x03c - 0x19d
|
||||
// y: 0x1fc - 0x2f7
|
||||
// 0x2f8 - 0x3f3
|
||||
picohw_state PicoPicohw;
|
||||
|
||||
static int prev_line_cnt_irq3 = 0, prev_line_cnt_irq5 = 0;
|
||||
static int fifo_bytes_line = (16000<<16)/60/262/2;
|
||||
|
||||
static const int guessed_rates[] = { 8000, 14000, 12000, 14000, 16000, 18000, 16000, 16000 }; // ?
|
||||
|
||||
#define PICOHW_FIFO_IRQ_THRESHOLD 12
|
||||
|
||||
PICO_INTERNAL void PicoReratePico(void)
|
||||
{
|
||||
int rate = guessed_rates[PicoPicohw.r12 & 7];
|
||||
if (Pico.m.pal)
|
||||
fifo_bytes_line = (rate<<16)/50/312/2;
|
||||
else fifo_bytes_line = (rate<<16)/60/262/2;
|
||||
PicoPicoPCMRerate(rate);
|
||||
}
|
||||
|
||||
static void PicoLinePico(void)
|
||||
{
|
||||
PicoPicohw.line_counter++;
|
||||
|
||||
#if 1
|
||||
if ((PicoPicohw.r12 & 0x4003) && PicoPicohw.line_counter - prev_line_cnt_irq3 > 200) {
|
||||
prev_line_cnt_irq3 = PicoPicohw.line_counter;
|
||||
// just a guess/hack, allows 101 Dalmantians to boot
|
||||
elprintf(EL_PICOHW, "irq3");
|
||||
SekInterrupt(3);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (PicoPicohw.fifo_bytes > 0)
|
||||
{
|
||||
PicoPicohw.fifo_line_bytes += fifo_bytes_line;
|
||||
if (PicoPicohw.fifo_line_bytes >= (1<<16)) {
|
||||
PicoPicohw.fifo_bytes -= PicoPicohw.fifo_line_bytes >> 16;
|
||||
PicoPicohw.fifo_line_bytes &= 0xffff;
|
||||
if (PicoPicohw.fifo_bytes < 0)
|
||||
PicoPicohw.fifo_bytes = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
PicoPicohw.fifo_line_bytes = 0;
|
||||
|
||||
#if 1
|
||||
if (PicoPicohw.fifo_bytes_prev >= PICOHW_FIFO_IRQ_THRESHOLD &&
|
||||
PicoPicohw.fifo_bytes < PICOHW_FIFO_IRQ_THRESHOLD) {
|
||||
prev_line_cnt_irq3 = PicoPicohw.line_counter; // ?
|
||||
elprintf(EL_PICOHW, "irq3, fb=%i", PicoPicohw.fifo_bytes);
|
||||
SekInterrupt(3);
|
||||
}
|
||||
PicoPicohw.fifo_bytes_prev = PicoPicohw.fifo_bytes;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (PicoPicohw.line_counter - prev_line_cnt_irq5 > 512) {
|
||||
prev_line_cnt_irq5 = PicoPicohw.line_counter;
|
||||
elprintf(EL_PICOHW, "irq5");
|
||||
SekInterrupt(5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PicoResetPico(void)
|
||||
{
|
||||
PicoPicoPCMReset();
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoInitPico(void)
|
||||
{
|
||||
elprintf(EL_STATUS, "Pico startup");
|
||||
PicoLineHook = PicoLinePico;
|
||||
PicoResetHook = PicoResetPico;
|
||||
|
||||
PicoAHW = PAHW_PICO;
|
||||
memset(&PicoPicohw, 0, sizeof(PicoPicohw));
|
||||
PicoPicohw.pen_pos[0] = 0x03c + 320/2;
|
||||
PicoPicohw.pen_pos[1] = 0x200 + 240/2;
|
||||
prev_line_cnt_irq3 = prev_line_cnt_irq5 = 0;
|
||||
|
||||
// map version register
|
||||
PicoDetectRegion();
|
||||
switch (Pico.m.hardware >> 6) {
|
||||
case 0: PicoPicohw.r1 = 0x00; break;
|
||||
case 1: PicoPicohw.r1 = 0x00; break;
|
||||
case 2: PicoPicohw.r1 = 0x40; break;
|
||||
case 3: PicoPicohw.r1 = 0x20; break;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*
|
||||
* The following ADPCM algorithm was stolen from MAME aica driver.
|
||||
* I'm quite sure it's not the right one, but it's the
|
||||
* best sounding of the ones that I tried.
|
||||
*/
|
||||
|
||||
#include "../pico_int.h"
|
||||
|
||||
#define ADPCMSHIFT 8
|
||||
#define ADFIX(f) (int) ((double)f * (double)(1<<ADPCMSHIFT))
|
||||
|
||||
/* limitter */
|
||||
#define Limit(val, max, min) { \
|
||||
if ( val > max ) val = max; \
|
||||
else if ( val < min ) val = min; \
|
||||
}
|
||||
|
||||
static const int TableQuant[8] =
|
||||
{
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(1.19921875),
|
||||
ADFIX(1.59765625),
|
||||
ADFIX(2.0),
|
||||
ADFIX(2.3984375)
|
||||
};
|
||||
|
||||
// changed using trial and error..
|
||||
//static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15 };
|
||||
static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, -1, -1, -3, -5, -7, -9, -11, -13, -15 };
|
||||
|
||||
static int sample = 0, quant = 0, sgn = 0;
|
||||
static int stepsamples = (44100<<10)/16000;
|
||||
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMReset(void)
|
||||
{
|
||||
sample = sgn = 0;
|
||||
quant = 0x7f;
|
||||
memset(PicoPicohw.xpcm_buffer, 0, sizeof(PicoPicohw.xpcm_buffer));
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate)
|
||||
{
|
||||
stepsamples = (PsndRate<<10)/xpcm_rate;
|
||||
}
|
||||
|
||||
#define XSHIFT 6
|
||||
|
||||
#define do_sample() \
|
||||
{ \
|
||||
int delta = quant * quant_mul[srcval] >> XSHIFT; \
|
||||
sample += delta - (delta >> 2); /* 3/4 */ \
|
||||
quant = (quant * TableQuant[srcval&7]) >> ADPCMSHIFT; \
|
||||
Limit(quant, 0x6000, 0x7f); \
|
||||
Limit(sample, 32767*3/4, -32768*3/4); \
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
|
||||
{
|
||||
unsigned char *src = PicoPicohw.xpcm_buffer;
|
||||
unsigned char *lim = PicoPicohw.xpcm_ptr;
|
||||
int srcval, needsamples = 0;
|
||||
|
||||
if (src == lim) goto end;
|
||||
|
||||
for (; length > 0 && src < lim; src++)
|
||||
{
|
||||
srcval = *src >> 4;
|
||||
do_sample();
|
||||
|
||||
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
|
||||
*buffer++ += sample;
|
||||
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
|
||||
}
|
||||
|
||||
srcval = *src & 0xf;
|
||||
do_sample();
|
||||
|
||||
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
|
||||
*buffer++ += sample;
|
||||
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
|
||||
}
|
||||
|
||||
// lame normalization stuff, needed due to wrong adpcm algo
|
||||
sgn += (sample < 0) ? -1 : 1;
|
||||
if (sgn < -16 || sgn > 16) sample -= sample >> 5;
|
||||
}
|
||||
|
||||
if (src < lim) {
|
||||
int di = lim - src;
|
||||
memmove(PicoPicohw.xpcm_buffer, src, di);
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
|
||||
elprintf(EL_PICOHW, "xpcm update: over %i", di);
|
||||
// adjust fifo
|
||||
PicoPicohw.fifo_bytes = di;
|
||||
return;
|
||||
}
|
||||
|
||||
elprintf(EL_PICOHW, "xpcm update: under %i", length);
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
|
||||
|
||||
end:
|
||||
if (stereo)
|
||||
// still must expand SN76496 to stereo
|
||||
for (; length > 0; buffer+=2, length--)
|
||||
buffer[1] = buffer[0];
|
||||
|
||||
sample = sgn = 0;
|
||||
quant = 0x7f;
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* common code for base/cd/32x
|
||||
* (C) notaz, 2007-2009,2013
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#define CYCLES_M68K_LINE 488 // suitable for both PAL/NTSC
|
||||
#define CYCLES_M68K_VINT_LAG 68
|
||||
#define CYCLES_M68K_ASD 148
|
||||
|
||||
// pad delay (for 6 button pads)
|
||||
#define PAD_DELAY() { \
|
||||
if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; \
|
||||
if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; \
|
||||
}
|
||||
|
||||
// CPUS_RUN
|
||||
#ifndef CPUS_RUN
|
||||
#define CPUS_RUN(m68k_cycles) \
|
||||
SekRunM68k(m68k_cycles)
|
||||
#endif
|
||||
|
||||
// sync m68k to SekCycleAim
|
||||
static void SekSyncM68k(void)
|
||||
{
|
||||
int cyc_do;
|
||||
pprof_start(m68k);
|
||||
pevt_log_m68k_o(EVT_RUN_START);
|
||||
|
||||
while ((cyc_do = SekCycleAim - SekCycleCnt) > 0) {
|
||||
SekCycleCnt += cyc_do;
|
||||
|
||||
#if defined(EMU_C68K)
|
||||
PicoCpuCM68k.cycles = cyc_do;
|
||||
CycloneRun(&PicoCpuCM68k);
|
||||
SekCycleCnt -= PicoCpuCM68k.cycles;
|
||||
#elif defined(EMU_M68K)
|
||||
SekCycleCnt += m68k_execute(cyc_do) - cyc_do;
|
||||
#elif defined(EMU_F68K)
|
||||
SekCycleCnt += fm68k_emulate(cyc_do, 0) - cyc_do;
|
||||
#endif
|
||||
}
|
||||
|
||||
SekCyclesLeft = 0;
|
||||
|
||||
SekTrace(0);
|
||||
pevt_log_m68k_o(EVT_RUN_END);
|
||||
pprof_end(m68k);
|
||||
}
|
||||
|
||||
static inline void SekRunM68k(int cyc)
|
||||
{
|
||||
SekCycleAim += cyc;
|
||||
SekSyncM68k();
|
||||
}
|
||||
|
||||
static int PicoFrameHints(void)
|
||||
{
|
||||
struct PicoVideo *pv=&Pico.video;
|
||||
int lines, y, lines_vis = 224, line_sample, skip, vcnt_wrap;
|
||||
unsigned int cycles;
|
||||
int hint; // Hint counter
|
||||
|
||||
pevt_log_m68k_o(EVT_FRAME_START);
|
||||
pv->v_counter = Pico.m.scanline = 0;
|
||||
|
||||
if ((PicoOpt&POPT_ALT_RENDERER) && !PicoSkipFrame && (pv->reg[1]&0x40)) { // fast rend., display enabled
|
||||
// draw a frame just after vblank in alternative render mode
|
||||
// yes, this will cause 1 frame lag, but this is inaccurate mode anyway.
|
||||
PicoFrameFull();
|
||||
#ifdef DRAW_FINISH_FUNC
|
||||
DRAW_FINISH_FUNC();
|
||||
#endif
|
||||
skip = 1;
|
||||
}
|
||||
else skip=PicoSkipFrame;
|
||||
|
||||
if (Pico.m.pal) {
|
||||
line_sample = 68;
|
||||
if (pv->reg[1]&8) lines_vis = 240;
|
||||
} else {
|
||||
line_sample = 93;
|
||||
}
|
||||
|
||||
z80_resetCycles();
|
||||
PsndDacLine = 0;
|
||||
emustatus &= ~1;
|
||||
|
||||
pv->status&=~0x88; // clear V-Int, come out of vblank
|
||||
|
||||
hint=pv->reg[10]; // Load H-Int counter
|
||||
//dprintf("-hint: %i", hint);
|
||||
|
||||
// This is to make active scan longer (needed for Double Dragon 2, mainly)
|
||||
CPUS_RUN(CYCLES_M68K_ASD);
|
||||
|
||||
for (y = 0; y < lines_vis; y++)
|
||||
{
|
||||
pv->v_counter = Pico.m.scanline = y;
|
||||
if ((pv->reg[12]&6) == 6) { // interlace mode 2
|
||||
pv->v_counter <<= 1;
|
||||
pv->v_counter |= pv->v_counter >> 8;
|
||||
pv->v_counter &= 0xff;
|
||||
}
|
||||
|
||||
// VDP FIFO
|
||||
pv->lwrite_cnt -= 12;
|
||||
if (pv->lwrite_cnt <= 0) {
|
||||
pv->lwrite_cnt=0;
|
||||
Pico.video.status|=0x200;
|
||||
}
|
||||
|
||||
PAD_DELAY();
|
||||
|
||||
// H-Interrupts:
|
||||
if (--hint < 0) // y <= lines_vis: Comix Zone, Golden Axe
|
||||
{
|
||||
hint=pv->reg[10]; // Reload H-Int counter
|
||||
pv->pending_ints|=0x10;
|
||||
if (pv->reg[0]&0x10) {
|
||||
elprintf(EL_INTS, "hint: @ %06x [%i]", SekPc, SekCyclesDone());
|
||||
SekInterrupt(4);
|
||||
}
|
||||
}
|
||||
|
||||
// decide if we draw this line
|
||||
if (!skip && (PicoOpt & POPT_ALT_RENDERER))
|
||||
{
|
||||
// find the right moment for frame renderer, when display is no longer blanked
|
||||
if ((pv->reg[1]&0x40) || y > 100) {
|
||||
PicoFrameFull();
|
||||
#ifdef DRAW_FINISH_FUNC
|
||||
DRAW_FINISH_FUNC();
|
||||
#endif
|
||||
skip = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// get samples from sound chips
|
||||
if ((y == 224 || y == line_sample) && PsndOut)
|
||||
{
|
||||
cycles = SekCyclesDone();
|
||||
|
||||
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80))
|
||||
PicoSyncZ80(cycles);
|
||||
if (ym2612.dacen && PsndDacLine <= y)
|
||||
PsndDoDAC(y);
|
||||
#ifdef PICO_CD
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
pcd_sync_s68k(cycles, 0);
|
||||
#endif
|
||||
#ifdef PICO_32X
|
||||
p32x_sync_sh2s(cycles);
|
||||
#endif
|
||||
PsndGetSamples(y);
|
||||
}
|
||||
|
||||
// Run scanline:
|
||||
line_base_cycles = SekCyclesDone();
|
||||
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
|
||||
CPUS_RUN(CYCLES_M68K_LINE);
|
||||
|
||||
if (PicoLineHook) PicoLineHook();
|
||||
pevt_log_m68k_o(EVT_NEXT_LINE);
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
if (DrawScanline < y)
|
||||
PicoDrawSync(y - 1, 0);
|
||||
#ifdef DRAW_FINISH_FUNC
|
||||
DRAW_FINISH_FUNC();
|
||||
#endif
|
||||
}
|
||||
|
||||
// V-int line (224 or 240)
|
||||
Pico.m.scanline = y;
|
||||
pv->v_counter = 0xe0; // bad for 240 mode
|
||||
if ((pv->reg[12]&6) == 6) pv->v_counter = 0xc1;
|
||||
|
||||
// VDP FIFO
|
||||
pv->lwrite_cnt=0;
|
||||
Pico.video.status|=0x200;
|
||||
|
||||
memcpy(PicoPadInt, PicoPad, sizeof(PicoPadInt));
|
||||
PAD_DELAY();
|
||||
|
||||
// Last H-Int:
|
||||
if (--hint < 0)
|
||||
{
|
||||
hint=pv->reg[10]; // Reload H-Int counter
|
||||
pv->pending_ints|=0x10;
|
||||
//printf("rhint: %i @ %06x [%i|%i]\n", hint, SekPc, y, SekCyclesDone());
|
||||
if (pv->reg[0]&0x10) SekInterrupt(4);
|
||||
}
|
||||
|
||||
pv->status|=0x08; // go into vblank
|
||||
pv->pending_ints|=0x20;
|
||||
|
||||
// the following SekRun is there for several reasons:
|
||||
// there must be a delay after vblank bit is set and irq is asserted (Mazin Saga)
|
||||
// also delay between F bit (bit 7) is set in SR and IRQ happens (Ex-Mutants)
|
||||
// also delay between last H-int and V-int (Golden Axe 3)
|
||||
line_base_cycles = SekCyclesDone();
|
||||
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
|
||||
CPUS_RUN(CYCLES_M68K_VINT_LAG);
|
||||
|
||||
if (pv->reg[1]&0x20) {
|
||||
elprintf(EL_INTS, "vint: @ %06x [%i]", SekPc, SekCyclesDone());
|
||||
SekInterrupt(6);
|
||||
}
|
||||
|
||||
cycles = SekCyclesDone();
|
||||
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80)) {
|
||||
PicoSyncZ80(cycles);
|
||||
elprintf(EL_INTS, "zint");
|
||||
z80_int();
|
||||
}
|
||||
|
||||
#ifdef PICO_CD
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
pcd_sync_s68k(cycles, 0);
|
||||
#endif
|
||||
#ifdef PICO_32X
|
||||
p32x_sync_sh2s(cycles);
|
||||
p32x_start_blank();
|
||||
#endif
|
||||
|
||||
// get samples from sound chips
|
||||
if (y == 224 && PsndOut)
|
||||
{
|
||||
if (ym2612.dacen && PsndDacLine <= y)
|
||||
PsndDoDAC(y);
|
||||
PsndGetSamples(y);
|
||||
}
|
||||
|
||||
// Run scanline:
|
||||
CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG - CYCLES_M68K_ASD);
|
||||
|
||||
if (PicoLineHook) PicoLineHook();
|
||||
pevt_log_m68k_o(EVT_NEXT_LINE);
|
||||
|
||||
lines = scanlines_total;
|
||||
vcnt_wrap = Pico.m.pal ? 0x103 : 0xEB; // based on Gens, TODO: verify
|
||||
|
||||
for (y++; y < lines; y++)
|
||||
{
|
||||
pv->v_counter = Pico.m.scanline = y;
|
||||
if (y >= vcnt_wrap)
|
||||
pv->v_counter -= Pico.m.pal ? 56 : 6;
|
||||
if ((pv->reg[12]&6) == 6)
|
||||
pv->v_counter = (pv->v_counter << 1) | 1;
|
||||
pv->v_counter &= 0xff;
|
||||
|
||||
PAD_DELAY();
|
||||
|
||||
// Run scanline:
|
||||
line_base_cycles = SekCyclesDone();
|
||||
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
|
||||
CPUS_RUN(CYCLES_M68K_LINE);
|
||||
|
||||
if (PicoLineHook) PicoLineHook();
|
||||
pevt_log_m68k_o(EVT_NEXT_LINE);
|
||||
}
|
||||
|
||||
// sync cpus
|
||||
cycles = SekCyclesDone();
|
||||
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80))
|
||||
PicoSyncZ80(cycles);
|
||||
if (PsndOut && ym2612.dacen && PsndDacLine <= lines-1)
|
||||
PsndDoDAC(lines-1);
|
||||
|
||||
#ifdef PICO_CD
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
pcd_sync_s68k(cycles, 0);
|
||||
#endif
|
||||
#ifdef PICO_32X
|
||||
p32x_sync_sh2s(cycles);
|
||||
#endif
|
||||
timers_cycle();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef PAD_DELAY
|
||||
#undef CPUS_RUN
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2009
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
#include "memory.h"
|
||||
|
||||
|
||||
unsigned int SekCycleCnt;
|
||||
unsigned int SekCycleAim;
|
||||
|
||||
|
||||
/* context */
|
||||
// Cyclone 68000
|
||||
#ifdef EMU_C68K
|
||||
struct Cyclone PicoCpuCM68k;
|
||||
#endif
|
||||
// MUSASHI 68000
|
||||
#ifdef EMU_M68K
|
||||
m68ki_cpu_core PicoCpuMM68k;
|
||||
#endif
|
||||
// FAME 68000
|
||||
#ifdef EMU_F68K
|
||||
M68K_CONTEXT PicoCpuFM68k;
|
||||
#endif
|
||||
|
||||
|
||||
/* callbacks */
|
||||
#ifdef EMU_C68K
|
||||
// interrupt acknowledgment
|
||||
static int SekIntAck(int level)
|
||||
{
|
||||
// try to emulate VDP's reaction to 68000 int ack
|
||||
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
||||
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
||||
PicoCpuCM68k.irq = 0;
|
||||
return CYCLONE_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
static void SekResetAck(void)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "Reset encountered @ %06x", SekPc);
|
||||
}
|
||||
|
||||
static int SekUnrecognizedOpcode()
|
||||
{
|
||||
unsigned int pc;
|
||||
pc = SekPc;
|
||||
elprintf(EL_ANOMALY, "Unrecognized Opcode @ %06x", pc);
|
||||
// see if we are still in a mapped region
|
||||
pc &= 0x00ffffff;
|
||||
if (map_flag_set(m68k_read16_map[pc >> M68K_MEM_SHIFT])) {
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "m68k crash @%06x", pc);
|
||||
PicoCpuCM68k.cycles = 0;
|
||||
PicoCpuCM68k.state_flags |= 1;
|
||||
return 1;
|
||||
}
|
||||
#ifdef EMU_M68K // debugging cyclone
|
||||
{
|
||||
extern int have_illegal;
|
||||
have_illegal = 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EMU_M68K
|
||||
static int SekIntAckM68K(int level)
|
||||
{
|
||||
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
||||
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
||||
CPU_INT_LEVEL = 0;
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
static int SekTasCallback(void)
|
||||
{
|
||||
return 0; // no writeback
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EMU_F68K
|
||||
static void SekIntAckF68K(unsigned level)
|
||||
{
|
||||
if (level == 4) {
|
||||
Pico.video.pending_ints = 0;
|
||||
elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCyclesDone());
|
||||
}
|
||||
else if(level == 6) {
|
||||
Pico.video.pending_ints &= ~0x20;
|
||||
elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCyclesDone());
|
||||
}
|
||||
PicoCpuFM68k.interrupts[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
PICO_INTERNAL void SekInit(void)
|
||||
{
|
||||
#ifdef EMU_C68K
|
||||
CycloneInit();
|
||||
memset(&PicoCpuCM68k,0,sizeof(PicoCpuCM68k));
|
||||
PicoCpuCM68k.IrqCallback=SekIntAck;
|
||||
PicoCpuCM68k.ResetCallback=SekResetAck;
|
||||
PicoCpuCM68k.UnrecognizedCallback=SekUnrecognizedOpcode;
|
||||
PicoCpuCM68k.flags=4; // Z set
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
{
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
m68k_set_context(&PicoCpuMM68k);
|
||||
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
|
||||
m68k_init();
|
||||
m68k_set_int_ack_callback(SekIntAckM68K);
|
||||
m68k_set_tas_instr_callback(SekTasCallback);
|
||||
//m68k_pulse_reset();
|
||||
m68k_set_context(oldcontext);
|
||||
}
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
{
|
||||
void *oldcontext = g_m68kcontext;
|
||||
g_m68kcontext = &PicoCpuFM68k;
|
||||
memset(&PicoCpuFM68k, 0, sizeof(PicoCpuFM68k));
|
||||
fm68k_init();
|
||||
PicoCpuFM68k.iack_handler = SekIntAckF68K;
|
||||
PicoCpuFM68k.sr = 0x2704; // Z flag
|
||||
g_m68kcontext = oldcontext;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Reset the 68000:
|
||||
PICO_INTERNAL int SekReset(void)
|
||||
{
|
||||
if (Pico.rom==NULL) return 1;
|
||||
|
||||
#ifdef EMU_C68K
|
||||
CycloneReset(&PicoCpuCM68k);
|
||||
#endif
|
||||
#ifdef EMU_M68K
|
||||
m68k_set_context(&PicoCpuMM68k); // if we ever reset m68k, we always need it's context to be set
|
||||
m68ki_cpu.sp[0]=0;
|
||||
m68k_set_irq(0);
|
||||
m68k_pulse_reset();
|
||||
REG_USP = 0; // ?
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
{
|
||||
g_m68kcontext = &PicoCpuFM68k;
|
||||
fm68k_reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SekStepM68k(void)
|
||||
{
|
||||
SekCycleAim=SekCycleCnt+1;
|
||||
#if defined(EMU_CORE_DEBUG)
|
||||
SekCycleCnt+=CM_compareRun(1, 0);
|
||||
#elif defined(EMU_C68K)
|
||||
PicoCpuCM68k.cycles=1;
|
||||
CycloneRun(&PicoCpuCM68k);
|
||||
SekCycleCnt+=1-PicoCpuCM68k.cycles;
|
||||
#elif defined(EMU_M68K)
|
||||
SekCycleCnt+=m68k_execute(1);
|
||||
#elif defined(EMU_F68K)
|
||||
SekCycleCnt+=fm68k_emulate(1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_INTERNAL void SekSetRealTAS(int use_real)
|
||||
{
|
||||
#ifdef EMU_C68K
|
||||
CycloneSetRealTAS(use_real);
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
// TODO
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pack the cpu into a common format:
|
||||
// XXX: rename
|
||||
PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
|
||||
{
|
||||
unsigned int pc=0;
|
||||
|
||||
#if defined(EMU_C68K)
|
||||
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
|
||||
memcpy(cpu,context->d,0x40);
|
||||
pc=context->pc-context->membase;
|
||||
*(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
|
||||
*(unsigned int *)(cpu+0x48)=context->osp;
|
||||
cpu[0x4c] = context->irq;
|
||||
cpu[0x4d] = context->state_flags & 1;
|
||||
#elif defined(EMU_M68K)
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
|
||||
memcpy(cpu,m68ki_cpu_p->dar,0x40);
|
||||
pc=m68ki_cpu_p->pc;
|
||||
*(unsigned int *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
|
||||
*(unsigned int *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
|
||||
cpu[0x4c] = CPU_INT_LEVEL>>8;
|
||||
cpu[0x4d] = CPU_STOPPED;
|
||||
m68k_set_context(oldcontext);
|
||||
#elif defined(EMU_F68K)
|
||||
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
|
||||
memcpy(cpu,context->dreg,0x40);
|
||||
pc=context->pc;
|
||||
*(unsigned int *)(cpu+0x44)=context->sr;
|
||||
*(unsigned int *)(cpu+0x48)=context->asp;
|
||||
cpu[0x4c] = context->interrupts[0];
|
||||
cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
|
||||
#endif
|
||||
|
||||
*(unsigned int *)(cpu+0x40) = pc;
|
||||
*(unsigned int *)(cpu+0x50) =
|
||||
is_sub ? SekCycleCntS68k : SekCycleCnt;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub)
|
||||
{
|
||||
#if defined(EMU_C68K)
|
||||
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
|
||||
CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
|
||||
context->osp=*(unsigned int *)(cpu+0x48);
|
||||
memcpy(context->d,cpu,0x40);
|
||||
context->membase = 0;
|
||||
context->pc = *(unsigned int *)(cpu+0x40);
|
||||
CycloneUnpack(context, NULL); // rebase PC
|
||||
context->irq = cpu[0x4c];
|
||||
context->state_flags = 0;
|
||||
if (cpu[0x4d])
|
||||
context->state_flags |= 1;
|
||||
#elif defined(EMU_M68K)
|
||||
void *oldcontext = m68ki_cpu_p;
|
||||
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
|
||||
m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
|
||||
memcpy(m68ki_cpu_p->dar,cpu,0x40);
|
||||
m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
|
||||
m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
|
||||
CPU_INT_LEVEL = cpu[0x4c] << 8;
|
||||
CPU_STOPPED = cpu[0x4d];
|
||||
m68k_set_context(oldcontext);
|
||||
#elif defined(EMU_F68K)
|
||||
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
|
||||
memcpy(context->dreg,cpu,0x40);
|
||||
context->pc =*(unsigned int *)(cpu+0x40);
|
||||
context->sr =*(unsigned int *)(cpu+0x44);
|
||||
context->asp=*(unsigned int *)(cpu+0x48);
|
||||
context->interrupts[0] = cpu[0x4c];
|
||||
context->execinfo &= ~FM68K_HALTED;
|
||||
if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
|
||||
#endif
|
||||
if (is_sub)
|
||||
SekCycleCntS68k = *(unsigned int *)(cpu+0x50);
|
||||
else
|
||||
SekCycleCnt = *(unsigned int *)(cpu+0x50);
|
||||
}
|
||||
|
||||
|
||||
/* idle loop detection, not to be used in CD mode */
|
||||
#ifdef EMU_C68K
|
||||
#include "cpu/cyclone/tools/idle.h"
|
||||
#endif
|
||||
|
||||
static unsigned short **idledet_ptrs = NULL;
|
||||
static int idledet_count = 0, idledet_bads = 0;
|
||||
static int idledet_start_frame = 0;
|
||||
|
||||
#if 0
|
||||
#define IDLE_STATS 1
|
||||
unsigned int idlehit_addrs[128], idlehit_counts[128];
|
||||
|
||||
void SekRegisterIdleHit(unsigned int pc)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 127 && idlehit_addrs[i]; i++) {
|
||||
if (idlehit_addrs[i] == pc) {
|
||||
idlehit_counts[i]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
idlehit_addrs[i] = pc;
|
||||
idlehit_counts[i] = 1;
|
||||
idlehit_addrs[i+1] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SekInitIdleDet(void)
|
||||
{
|
||||
unsigned short **tmp = realloc(idledet_ptrs, 0x200*4);
|
||||
if (tmp == NULL) {
|
||||
free(idledet_ptrs);
|
||||
idledet_ptrs = NULL;
|
||||
}
|
||||
else
|
||||
idledet_ptrs = tmp;
|
||||
idledet_count = idledet_bads = 0;
|
||||
idledet_start_frame = Pico.m.frame_count + 360;
|
||||
#ifdef IDLE_STATS
|
||||
idlehit_addrs[0] = 0;
|
||||
#endif
|
||||
|
||||
#ifdef EMU_C68K
|
||||
CycloneInitIdle();
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
fm68k_emulate(0, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SekIsIdleReady(void)
|
||||
{
|
||||
return (Pico.m.frame_count >= idledet_start_frame);
|
||||
}
|
||||
|
||||
int SekIsIdleCode(unsigned short *dst, int bytes)
|
||||
{
|
||||
// printf("SekIsIdleCode %04x %i\n", *dst, bytes);
|
||||
switch (bytes)
|
||||
{
|
||||
case 2:
|
||||
if ((*dst & 0xf000) != 0x6000) // not another branch
|
||||
return 1;
|
||||
break;
|
||||
case 4:
|
||||
if ( (*dst & 0xff3f) == 0x4a38 || // tst.x ($xxxx.w); tas ($xxxx.w)
|
||||
(*dst & 0xc1ff) == 0x0038 || // move.x ($xxxx.w), dX
|
||||
(*dst & 0xf13f) == 0xb038) // cmp.x ($xxxx.w), dX
|
||||
return 1;
|
||||
if (PicoAHW & (PAHW_MCD|PAHW_32X))
|
||||
break;
|
||||
// with no addons, there should be no need to wait
|
||||
// for byte change anywhere
|
||||
if ( (*dst & 0xfff8) == 0x4a10 || // tst.b ($aX)
|
||||
(*dst & 0xfff8) == 0x4a28) // tst.b ($xxxx,a0)
|
||||
return 1;
|
||||
break;
|
||||
case 6:
|
||||
if ( ((dst[1] & 0xe0) == 0xe0 && ( // RAM and
|
||||
*dst == 0x4a39 || // tst.b ($xxxxxxxx)
|
||||
*dst == 0x4a79 || // tst.w ($xxxxxxxx)
|
||||
*dst == 0x4ab9 || // tst.l ($xxxxxxxx)
|
||||
(*dst & 0xc1ff) == 0x0039 || // move.x ($xxxxxxxx), dX
|
||||
(*dst & 0xf13f) == 0xb039))||// cmp.x ($xxxxxxxx), dX
|
||||
*dst == 0x0838 || // btst $X, ($xxxx.w) [6 byte op]
|
||||
(*dst & 0xffbf) == 0x0c38) // cmpi.{b,w} $X, ($xxxx.w)
|
||||
return 1;
|
||||
break;
|
||||
case 8:
|
||||
if ( ((dst[2] & 0xe0) == 0xe0 && ( // RAM and
|
||||
*dst == 0x0839 || // btst $X, ($xxxxxxxx.w) [8 byte op]
|
||||
(*dst & 0xffbf) == 0x0c39))||// cmpi.{b,w} $X, ($xxxxxxxx)
|
||||
*dst == 0x0cb8) // cmpi.l $X, ($xxxx.w)
|
||||
return 1;
|
||||
break;
|
||||
case 12:
|
||||
if (PicoAHW & (PAHW_MCD|PAHW_32X))
|
||||
break;
|
||||
if ( (*dst & 0xf1f8) == 0x3010 && // move.w (aX), dX
|
||||
(dst[1]&0xf100) == 0x0000 && // arithmetic
|
||||
(dst[3]&0xf100) == 0x0000) // arithmetic
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SekRegisterIdlePatch(unsigned int pc, int oldop, int newop, void *ctx)
|
||||
{
|
||||
int is_main68k = 1;
|
||||
u16 *target;
|
||||
uptr v;
|
||||
|
||||
#if defined(EMU_C68K)
|
||||
struct Cyclone *cyc = ctx;
|
||||
is_main68k = cyc == &PicoCpuCM68k;
|
||||
pc -= cyc->membase;
|
||||
#elif defined(EMU_F68K)
|
||||
is_main68k = ctx == &PicoCpuFM68k;
|
||||
#endif
|
||||
pc &= ~0xff000000;
|
||||
if (!(newop&0x200))
|
||||
elprintf(EL_IDLE, "idle: patch %06x %04x %04x %c %c #%i", pc, oldop, newop,
|
||||
(newop&0x200)?'n':'y', is_main68k?'m':'s', idledet_count);
|
||||
|
||||
// XXX: probably shouldn't patch RAM too
|
||||
v = m68k_read16_map[pc >> M68K_MEM_SHIFT];
|
||||
if (!(v & 0x80000000))
|
||||
target = (u16 *)((v << 1) + pc);
|
||||
else {
|
||||
if (++idledet_bads > 128)
|
||||
return 2; // remove detector
|
||||
return 1; // don't patch
|
||||
}
|
||||
|
||||
if (idledet_count >= 0x200 && (idledet_count & 0x1ff) == 0) {
|
||||
unsigned short **tmp = realloc(idledet_ptrs, (idledet_count+0x200)*4);
|
||||
if (tmp == NULL)
|
||||
return 1;
|
||||
idledet_ptrs = tmp;
|
||||
}
|
||||
|
||||
idledet_ptrs[idledet_count++] = target;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SekFinishIdleDet(void)
|
||||
{
|
||||
#ifdef EMU_C68K
|
||||
CycloneFinishIdle();
|
||||
#endif
|
||||
#ifdef EMU_F68K
|
||||
fm68k_emulate(0, 2);
|
||||
#endif
|
||||
while (idledet_count > 0)
|
||||
{
|
||||
unsigned short *op = idledet_ptrs[--idledet_count];
|
||||
if ((*op & 0xfd00) == 0x7100)
|
||||
*op &= 0xff, *op |= 0x6600;
|
||||
else if ((*op & 0xfd00) == 0x7500)
|
||||
*op &= 0xff, *op |= 0x6700;
|
||||
else if ((*op & 0xfd00) == 0x7d00)
|
||||
*op &= 0xff, *op |= 0x6000;
|
||||
else
|
||||
elprintf(EL_STATUS|EL_IDLE, "idle: don't know how to restore %04x", *op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(CPU_CMP_R) || defined(CPU_CMP_W)
|
||||
#include "debug.h"
|
||||
|
||||
struct ref_68k {
|
||||
u32 dar[16];
|
||||
u32 pc;
|
||||
u32 sr;
|
||||
u32 cycles;
|
||||
u32 pc_prev;
|
||||
};
|
||||
struct ref_68k ref_68ks[2];
|
||||
static int current_68k;
|
||||
|
||||
void SekTrace(int is_s68k)
|
||||
{
|
||||
struct ref_68k *x68k = &ref_68ks[is_s68k];
|
||||
u32 pc = is_s68k ? SekPcS68k : SekPc;
|
||||
u32 sr = is_s68k ? SekSrS68k : SekSr;
|
||||
u32 cycles = is_s68k ? SekCycleCntS68k : SekCycleCnt;
|
||||
u32 r;
|
||||
u8 cmd;
|
||||
#ifdef CPU_CMP_W
|
||||
int i;
|
||||
|
||||
if (is_s68k != current_68k) {
|
||||
current_68k = is_s68k;
|
||||
cmd = CTL_68K_SLAVE | current_68k;
|
||||
tl_write(&cmd, sizeof(cmd));
|
||||
}
|
||||
if (pc != x68k->pc) {
|
||||
x68k->pc = pc;
|
||||
tl_write_uint(CTL_68K_PC, x68k->pc);
|
||||
}
|
||||
if (sr != x68k->sr) {
|
||||
x68k->sr = sr;
|
||||
tl_write_uint(CTL_68K_SR, x68k->sr);
|
||||
}
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = is_s68k ? SekDarS68k(i) : SekDar(i);
|
||||
if (r != x68k->dar[i]) {
|
||||
x68k->dar[i] = r;
|
||||
tl_write_uint(CTL_68K_R + i, r);
|
||||
}
|
||||
}
|
||||
tl_write_uint(CTL_68K_CYCLES, cycles);
|
||||
#else
|
||||
int i, bad = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ret = tl_read(&cmd, sizeof(cmd));
|
||||
if (ret == 0) {
|
||||
elprintf(EL_STATUS, "EOF");
|
||||
exit(1);
|
||||
}
|
||||
switch (cmd) {
|
||||
case CTL_68K_SLAVE:
|
||||
case CTL_68K_SLAVE + 1:
|
||||
current_68k = cmd & 1;
|
||||
break;
|
||||
case CTL_68K_PC:
|
||||
tl_read_uint(&x68k->pc);
|
||||
break;
|
||||
case CTL_68K_SR:
|
||||
tl_read_uint(&x68k->sr);
|
||||
break;
|
||||
case CTL_68K_CYCLES:
|
||||
tl_read_uint(&x68k->cycles);
|
||||
goto breakloop;
|
||||
default:
|
||||
if (CTL_68K_R <= cmd && cmd < CTL_68K_R + 0x10)
|
||||
tl_read_uint(&x68k->dar[cmd - CTL_68K_R]);
|
||||
else
|
||||
elprintf(EL_STATUS, "invalid cmd: %02x", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
breakloop:
|
||||
if (is_s68k != current_68k) {
|
||||
printf("bad 68k: %d %d\n", is_s68k, current_68k);
|
||||
bad = 1;
|
||||
}
|
||||
if (cycles != x68k->cycles) {
|
||||
printf("bad cycles: %u %u\n", cycles, x68k->cycles);
|
||||
bad = 1;
|
||||
}
|
||||
if ((pc ^ x68k->pc) & 0xffffff) {
|
||||
printf("bad PC: %08x %08x\n", pc, x68k->pc);
|
||||
bad = 1;
|
||||
}
|
||||
if (sr != x68k->sr) {
|
||||
printf("bad SR: %03x %03x\n", sr, x68k->sr);
|
||||
bad = 1;
|
||||
}
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = is_s68k ? SekDarS68k(i) : SekDar(i);
|
||||
if (r != x68k->dar[i]) {
|
||||
printf("bad %c%d: %08x %08x\n", i < 8 ? 'D' : 'A', i & 7,
|
||||
r, x68k->dar[i]);
|
||||
bad = 1;
|
||||
}
|
||||
}
|
||||
if (bad) {
|
||||
for (i = 0; i < 8; i++)
|
||||
printf("D%d: %08x A%d: %08x\n", i, x68k->dar[i],
|
||||
i, x68k->dar[i + 8]);
|
||||
printf("PC: %08x, %08x\n", x68k->pc, x68k->pc_prev);
|
||||
|
||||
PDebugDumpMem();
|
||||
exit(1);
|
||||
}
|
||||
x68k->pc_prev = x68k->pc;
|
||||
#endif
|
||||
}
|
||||
#endif // CPU_CMP_*
|
||||
|
||||
#if defined(EMU_M68K) && M68K_INSTRUCTION_HOOK == OPT_SPECIFY_HANDLER
|
||||
static unsigned char op_flags[0x400000/2] = { 0, };
|
||||
static int atexit_set = 0;
|
||||
|
||||
static void make_idc(void)
|
||||
{
|
||||
FILE *f = fopen("idc.idc", "w");
|
||||
int i;
|
||||
if (!f) return;
|
||||
fprintf(f, "#include <idc.idc>\nstatic main() {\n");
|
||||
for (i = 0; i < 0x400000/2; i++)
|
||||
if (op_flags[i] != 0)
|
||||
fprintf(f, " MakeCode(0x%06x);\n", i*2);
|
||||
fprintf(f, "}\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void instruction_hook(void)
|
||||
{
|
||||
if (!atexit_set) {
|
||||
atexit(make_idc);
|
||||
atexit_set = 1;
|
||||
}
|
||||
if (REG_PC < 0x400000)
|
||||
op_flags[REG_PC/2] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* SMS emulation
|
||||
* (C) notaz, 2009-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
/*
|
||||
* TODO:
|
||||
* - start in a state as if BIOS ran
|
||||
* - remaining status flags (OVR/COL)
|
||||
* - RAM support in mapper
|
||||
* - region support
|
||||
* - SN76496 DAC-like usage
|
||||
* - H counter
|
||||
*/
|
||||
#include "pico_int.h"
|
||||
#include "memory.h"
|
||||
#include "sound/sn76496.h"
|
||||
|
||||
static unsigned char vdp_data_read(void)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
unsigned char d;
|
||||
|
||||
d = Pico.vramb[pv->addr];
|
||||
pv->addr = (pv->addr + 1) & 0x3fff;
|
||||
pv->pending = 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
static unsigned char vdp_ctl_read(void)
|
||||
{
|
||||
unsigned char d = Pico.video.pending_ints << 7;
|
||||
Pico.video.pending = 0;
|
||||
Pico.video.pending_ints = 0;
|
||||
|
||||
elprintf(EL_SR, "VDP sr: %02x", d);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void vdp_data_write(unsigned char d)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
|
||||
if (pv->type == 3) {
|
||||
Pico.cram[pv->addr & 0x1f] = d;
|
||||
Pico.m.dirtyPal = 1;
|
||||
} else {
|
||||
Pico.vramb[pv->addr] = d;
|
||||
}
|
||||
pv->addr = (pv->addr + 1) & 0x3fff;
|
||||
|
||||
pv->pending = 0;
|
||||
}
|
||||
|
||||
static void vdp_ctl_write(unsigned char d)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
|
||||
if (pv->pending) {
|
||||
if ((d >> 6) == 2) {
|
||||
pv->reg[d & 0x0f] = pv->addr;
|
||||
elprintf(EL_IO, " VDP r%02x=%02x", d & 0x0f, pv->addr & 0xff);
|
||||
}
|
||||
pv->type = d >> 6;
|
||||
pv->addr &= 0x00ff;
|
||||
pv->addr |= (d & 0x3f) << 8;
|
||||
} else {
|
||||
pv->addr &= 0x3f00;
|
||||
pv->addr |= d;
|
||||
}
|
||||
pv->pending ^= 1;
|
||||
}
|
||||
|
||||
static unsigned char z80_sms_in(unsigned short a)
|
||||
{
|
||||
unsigned char d = 0;
|
||||
|
||||
elprintf(EL_IO, "z80 port %04x read", a);
|
||||
a &= 0xc1;
|
||||
switch (a)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
d = 0xff;
|
||||
break;
|
||||
|
||||
case 0x40: /* V counter */
|
||||
d = Pico.video.v_counter;
|
||||
elprintf(EL_HVCNT, "V counter read: %02x", d);
|
||||
break;
|
||||
|
||||
case 0x41: /* H counter */
|
||||
d = Pico.m.rotate++;
|
||||
elprintf(EL_HVCNT, "H counter read: %02x", d);
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
d = vdp_data_read();
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
d = vdp_ctl_read();
|
||||
break;
|
||||
|
||||
case 0xc0: /* I/O port A and B */
|
||||
d = ~((PicoPad[0] & 0x3f) | (PicoPad[1] << 6));
|
||||
break;
|
||||
|
||||
case 0xc1: /* I/O port B and miscellaneous */
|
||||
d = (Pico.ms.io_ctl & 0x80) | ((Pico.ms.io_ctl << 1) & 0x40) | 0x30;
|
||||
d |= ~(PicoPad[1] >> 2) & 0x0f;
|
||||
break;
|
||||
}
|
||||
|
||||
elprintf(EL_IO, "ret = %02x", d);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void z80_sms_out(unsigned short a, unsigned char d)
|
||||
{
|
||||
elprintf(EL_IO, "z80 port %04x write %02x", a, d);
|
||||
a &= 0xc1;
|
||||
switch (a)
|
||||
{
|
||||
case 0x01:
|
||||
Pico.ms.io_ctl = d;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
if (PicoOpt & POPT_EN_PSG)
|
||||
SN76496Write(d);
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
vdp_data_write(d);
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
vdp_ctl_write(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int bank_mask;
|
||||
|
||||
static void write_bank(unsigned short a, unsigned char d)
|
||||
{
|
||||
elprintf(EL_Z80BNK, "bank %04x %02x @ %04x", a, d, z80_pc());
|
||||
switch (a & 0x0f)
|
||||
{
|
||||
case 0x0c:
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "%02x written to control reg!", d);
|
||||
break;
|
||||
case 0x0d:
|
||||
if (d != 0)
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "bank0 changed to %d!", d);
|
||||
break;
|
||||
case 0x0e:
|
||||
d &= bank_mask;
|
||||
z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0);
|
||||
#ifdef _USE_CZ80
|
||||
Cz80_Set_Fetch(&CZ80, 0x4000, 0x7fff, (FPTR)Pico.rom + (d << 14));
|
||||
#endif
|
||||
break;
|
||||
case 0x0f:
|
||||
d &= bank_mask;
|
||||
z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0);
|
||||
#ifdef _USE_CZ80
|
||||
Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.rom + (d << 14));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
Pico.ms.carthw[a & 0x0f] = d;
|
||||
}
|
||||
|
||||
static void xwrite(unsigned int a, unsigned char d)
|
||||
{
|
||||
elprintf(EL_IO, "z80 write [%04x] %02x", a, d);
|
||||
if (a >= 0xc000)
|
||||
Pico.zram[a & 0x1fff] = d;
|
||||
if (a >= 0xfff8)
|
||||
write_bank(a, d);
|
||||
}
|
||||
|
||||
void PicoResetMS(void)
|
||||
{
|
||||
z80_reset();
|
||||
PsndReset(); // pal must be known here
|
||||
}
|
||||
|
||||
void PicoPowerMS(void)
|
||||
{
|
||||
int s, tmp;
|
||||
|
||||
memset(&Pico.ram,0,(unsigned char *)&Pico.rom - Pico.ram);
|
||||
memset(&Pico.video,0,sizeof(Pico.video));
|
||||
memset(&Pico.m,0,sizeof(Pico.m));
|
||||
Pico.m.pal = 0;
|
||||
|
||||
// calculate a mask for bank writes.
|
||||
// ROM loader has aligned the size for us, so this is safe.
|
||||
s = 0; tmp = Pico.romsize;
|
||||
while ((tmp >>= 1) != 0)
|
||||
s++;
|
||||
if (Pico.romsize > (1 << s))
|
||||
s++;
|
||||
tmp = 1 << s;
|
||||
bank_mask = (tmp - 1) >> 14;
|
||||
|
||||
Pico.ms.carthw[0x0e] = 1;
|
||||
Pico.ms.carthw[0x0f] = 2;
|
||||
|
||||
PicoReset();
|
||||
}
|
||||
|
||||
void PicoMemSetupMS(void)
|
||||
{
|
||||
z80_map_set(z80_read_map, 0x0000, 0xbfff, Pico.rom, 0);
|
||||
z80_map_set(z80_read_map, 0xc000, 0xdfff, Pico.zram, 0);
|
||||
z80_map_set(z80_read_map, 0xe000, 0xffff, Pico.zram, 0);
|
||||
|
||||
z80_map_set(z80_write_map, 0x0000, 0xbfff, xwrite, 1);
|
||||
z80_map_set(z80_write_map, 0xc000, 0xdfff, Pico.zram, 0);
|
||||
z80_map_set(z80_write_map, 0xe000, 0xffff, xwrite, 1);
|
||||
|
||||
#ifdef _USE_DRZ80
|
||||
drZ80.z80_in = z80_sms_in;
|
||||
drZ80.z80_out = z80_sms_out;
|
||||
#endif
|
||||
#ifdef _USE_CZ80
|
||||
Cz80_Set_Fetch(&CZ80, 0x0000, 0xbfff, (FPTR)Pico.rom);
|
||||
Cz80_Set_Fetch(&CZ80, 0xc000, 0xdfff, (FPTR)Pico.zram);
|
||||
Cz80_Set_Fetch(&CZ80, 0xe000, 0xffff, (FPTR)Pico.zram);
|
||||
Cz80_Set_INPort(&CZ80, z80_sms_in);
|
||||
Cz80_Set_OUTPort(&CZ80, z80_sms_out);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PicoStateLoadedMS(void)
|
||||
{
|
||||
write_bank(0xfffe, Pico.ms.carthw[0x0e]);
|
||||
write_bank(0xffff, Pico.ms.carthw[0x0f]);
|
||||
}
|
||||
|
||||
void PicoFrameMS(void)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
int is_pal = Pico.m.pal;
|
||||
int lines = is_pal ? 313 : 262;
|
||||
int cycles_line = is_pal ? 58020 : 58293; /* (226.6 : 227.7) * 256 */
|
||||
int cycles_done = 0, cycles_aim = 0;
|
||||
int skip = PicoSkipFrame;
|
||||
int lines_vis = 192;
|
||||
int hint; // Hint counter
|
||||
int nmi;
|
||||
int y;
|
||||
|
||||
nmi = (PicoPad[0] >> 7) & 1;
|
||||
if (!Pico.ms.nmi_state && nmi)
|
||||
z80_nmi();
|
||||
Pico.ms.nmi_state = nmi;
|
||||
|
||||
PicoFrameStartMode4();
|
||||
hint = pv->reg[0x0a];
|
||||
|
||||
for (y = 0; y < lines; y++)
|
||||
{
|
||||
pv->v_counter = Pico.m.scanline = y;
|
||||
if (y > 218)
|
||||
pv->v_counter = y - 6;
|
||||
|
||||
if (y < lines_vis && !skip)
|
||||
PicoLineMode4(y);
|
||||
|
||||
if (y <= lines_vis)
|
||||
{
|
||||
if (--hint < 0)
|
||||
{
|
||||
hint = pv->reg[0x0a];
|
||||
pv->pending_ints |= 2;
|
||||
if (pv->reg[0] & 0x10) {
|
||||
elprintf(EL_INTS, "hint");
|
||||
z80_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (y == lines_vis + 1) {
|
||||
pv->pending_ints |= 1;
|
||||
if (pv->reg[1] & 0x20) {
|
||||
elprintf(EL_INTS, "vint");
|
||||
z80_int();
|
||||
}
|
||||
}
|
||||
|
||||
cycles_aim += cycles_line;
|
||||
cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8;
|
||||
}
|
||||
|
||||
if (PsndOut)
|
||||
PsndGetSamplesMS();
|
||||
}
|
||||
|
||||
void PicoFrameDrawOnlyMS(void)
|
||||
{
|
||||
int lines_vis = 192;
|
||||
int y;
|
||||
|
||||
PicoFrameStartMode4();
|
||||
|
||||
for (y = 0; y < lines_vis; y++)
|
||||
PicoLineMode4(y);
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* some code for sample mixing
|
||||
* (C) notaz, 2006,2007
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#define MAXOUT (+32767)
|
||||
#define MINOUT (-32768)
|
||||
|
||||
/* limitter */
|
||||
#define Limit(val, max,min) { \
|
||||
if ( val > max ) val = max; \
|
||||
else if ( val < min ) val = min; \
|
||||
}
|
||||
|
||||
|
||||
void mix_32_to_16l_stereo(short *dest, int *src, int count)
|
||||
{
|
||||
int l, r;
|
||||
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
l = r = *dest;
|
||||
l += *src++;
|
||||
r += *src++;
|
||||
Limit( l, MAXOUT, MINOUT );
|
||||
Limit( r, MAXOUT, MINOUT );
|
||||
*dest++ = l;
|
||||
*dest++ = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mix_32_to_16_mono(short *dest, int *src, int count)
|
||||
{
|
||||
int l;
|
||||
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
l = *dest;
|
||||
l += *src++;
|
||||
Limit( l, MAXOUT, MINOUT );
|
||||
*dest++ = l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mix_16h_to_32(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
void mix_16h_to_32_s1(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
count >>= 1;
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
mp3_buf += 1*2;
|
||||
}
|
||||
}
|
||||
|
||||
void mix_16h_to_32_s2(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
count >>= 1;
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
mp3_buf += 3*2;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
//void mix_32_to_32(int *dest, int *src, int count);
|
||||
void mix_16h_to_32(int *dest, short *src, int count);
|
||||
void mix_16h_to_32_s1(int *dest, short *src, int count);
|
||||
void mix_16h_to_32_s2(int *dest, short *src, int count);
|
||||
void mix_32_to_16l_stereo(short *dest, int *src, int count);
|
||||
void mix_32_to_16_mono(short *dest, int *src, int count);
|
||||
|
||||
extern int mix_32_to_16l_level;
|
||||
void mix_32_to_16l_stereo_lvl(short *dest, int *src, int count);
|
|
@ -0,0 +1,344 @@
|
|||
/***************************************************************************
|
||||
|
||||
sn76496.c
|
||||
|
||||
Routines to emulate the Texas Instruments SN76489 / SN76496 programmable
|
||||
tone /noise generator. Also known as (or at least compatible with) TMS9919.
|
||||
|
||||
Noise emulation is not accurate due to lack of documentation. The noise
|
||||
generator uses a shift register with a XOR-feedback network, but the exact
|
||||
layout is unknown. It can be set for either period or white noise; again,
|
||||
the details are unknown.
|
||||
|
||||
28/03/2005 : Sebastien Chevalier
|
||||
Update th SN76496Write func, according to SN76489 doc found on SMSPower.
|
||||
- On write with 0x80 set to 0, when LastRegister is other then TONE,
|
||||
the function is similar than update with 0x80 set to 1
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef __GNUC__
|
||||
#pragma warning (disable:4244)
|
||||
#endif
|
||||
|
||||
#include "sn76496.h"
|
||||
|
||||
#define MAX_OUTPUT 0x47ff // was 0x7fff
|
||||
|
||||
#define STEP 0x10000
|
||||
|
||||
|
||||
/* Formulas for noise generator */
|
||||
/* bit0 = output */
|
||||
|
||||
/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
|
||||
#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
|
||||
|
||||
/* noise feedback for periodic noise mode */
|
||||
//#define FB_PNOISE 0x10000 /* 16bit rorate */
|
||||
#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */
|
||||
|
||||
/*
|
||||
0x08000 is definitely wrong. The Master System conversion of Marble Madness
|
||||
uses periodic noise as a baseline. With a 15-bit rotate, the bassline is
|
||||
out of tune.
|
||||
The 16-bit rotate has been confirmed against a real PAL Sega Master System 2.
|
||||
Hope that helps the System E stuff, more news on the PSG as and when!
|
||||
*/
|
||||
|
||||
/* noise generator start preset (for periodic noise) */
|
||||
#define NG_PRESET 0x0f35
|
||||
|
||||
|
||||
struct SN76496
|
||||
{
|
||||
//sound_stream * Channel;
|
||||
int SampleRate;
|
||||
unsigned int UpdateStep;
|
||||
int VolTable[16]; /* volume table */
|
||||
int Register[8]; /* registers */
|
||||
int LastRegister; /* last register written */
|
||||
int Volume[4]; /* volume of voice 0-2 and noise */
|
||||
unsigned int RNG; /* noise generator */
|
||||
int NoiseFB; /* noise feedback mask */
|
||||
int Period[4];
|
||||
int Count[4];
|
||||
int Output[4];
|
||||
int pad[1];
|
||||
};
|
||||
|
||||
static struct SN76496 ono_sn; // one and only SN76496
|
||||
int *sn76496_regs;
|
||||
|
||||
//static
|
||||
void SN76496Write(int data)
|
||||
{
|
||||
struct SN76496 *R = &ono_sn;
|
||||
int n;
|
||||
|
||||
|
||||
/* update the output buffer before changing the registers */
|
||||
//stream_update(R->Channel,0);
|
||||
|
||||
if (data & 0x80)
|
||||
{
|
||||
int r = (data & 0x70) >> 4;
|
||||
int c = r/2;
|
||||
|
||||
R->LastRegister = r;
|
||||
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
|
||||
switch (r)
|
||||
{
|
||||
case 0: /* tone 0 : frequency */
|
||||
case 2: /* tone 1 : frequency */
|
||||
case 4: /* tone 2 : frequency */
|
||||
R->Period[c] = R->UpdateStep * R->Register[r];
|
||||
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
|
||||
if (r == 4)
|
||||
{
|
||||
/* update noise shift frequency */
|
||||
if ((R->Register[6] & 0x03) == 0x03)
|
||||
R->Period[3] = 2 * R->Period[2];
|
||||
}
|
||||
break;
|
||||
case 1: /* tone 0 : volume */
|
||||
case 3: /* tone 1 : volume */
|
||||
case 5: /* tone 2 : volume */
|
||||
case 7: /* noise : volume */
|
||||
R->Volume[c] = R->VolTable[data & 0x0f];
|
||||
break;
|
||||
case 6: /* noise : frequency, mode */
|
||||
{
|
||||
int n = R->Register[6];
|
||||
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
|
||||
n &= 3;
|
||||
/* N/512,N/1024,N/2048,Tone #3 output */
|
||||
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
|
||||
|
||||
/* reset noise shifter */
|
||||
R->RNG = NG_PRESET;
|
||||
R->Output[3] = R->RNG & 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int r = R->LastRegister;
|
||||
int c = r/2;
|
||||
|
||||
switch (r)
|
||||
{
|
||||
case 0: /* tone 0 : frequency */
|
||||
case 2: /* tone 1 : frequency */
|
||||
case 4: /* tone 2 : frequency */
|
||||
R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
|
||||
R->Period[c] = R->UpdateStep * R->Register[r];
|
||||
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
|
||||
if (r == 4)
|
||||
{
|
||||
/* update noise shift frequency */
|
||||
if ((R->Register[6] & 0x03) == 0x03)
|
||||
R->Period[3] = 2 * R->Period[2];
|
||||
}
|
||||
break;
|
||||
case 1: /* tone 0 : volume */
|
||||
case 3: /* tone 1 : volume */
|
||||
case 5: /* tone 2 : volume */
|
||||
case 7: /* noise : volume */
|
||||
R->Volume[c] = R->VolTable[data & 0x0f];
|
||||
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
|
||||
break;
|
||||
case 6: /* noise : frequency, mode */
|
||||
{
|
||||
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
|
||||
n = R->Register[6];
|
||||
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
|
||||
n &= 3;
|
||||
/* N/512,N/1024,N/2048,Tone #3 output */
|
||||
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
|
||||
|
||||
/* reset noise shifter */
|
||||
R->RNG = NG_PRESET;
|
||||
R->Output[3] = R->RNG & 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
WRITE8_HANDLER( SN76496_0_w ) { SN76496Write(0,data); }
|
||||
WRITE8_HANDLER( SN76496_1_w ) { SN76496Write(1,data); }
|
||||
WRITE8_HANDLER( SN76496_2_w ) { SN76496Write(2,data); }
|
||||
WRITE8_HANDLER( SN76496_3_w ) { SN76496Write(3,data); }
|
||||
WRITE8_HANDLER( SN76496_4_w ) { SN76496Write(4,data); }
|
||||
*/
|
||||
|
||||
//static
|
||||
void SN76496Update(short *buffer, int length, int stereo)
|
||||
{
|
||||
int i;
|
||||
struct SN76496 *R = &ono_sn;
|
||||
|
||||
/* If the volume is 0, increase the counter */
|
||||
for (i = 0;i < 4;i++)
|
||||
{
|
||||
if (R->Volume[i] == 0)
|
||||
{
|
||||
/* note that I do count += length, NOT count = length + 1. You might think */
|
||||
/* it's the same since the volume is 0, but doing the latter could cause */
|
||||
/* interferencies when the program is rapidly modulating the volume. */
|
||||
if (R->Count[i] <= length*STEP) R->Count[i] += length*STEP;
|
||||
}
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
int vol[4];
|
||||
unsigned int out;
|
||||
int left;
|
||||
|
||||
|
||||
/* vol[] keeps track of how long each square wave stays */
|
||||
/* in the 1 position during the sample period. */
|
||||
vol[0] = vol[1] = vol[2] = vol[3] = 0;
|
||||
|
||||
for (i = 0;i < 3;i++)
|
||||
{
|
||||
if (R->Output[i]) vol[i] += R->Count[i];
|
||||
R->Count[i] -= STEP;
|
||||
/* Period[i] is the half period of the square wave. Here, in each */
|
||||
/* loop I add Period[i] twice, so that at the end of the loop the */
|
||||
/* square wave is in the same status (0 or 1) it was at the start. */
|
||||
/* vol[i] is also incremented by Period[i], since the wave has been 1 */
|
||||
/* exactly half of the time, regardless of the initial position. */
|
||||
/* If we exit the loop in the middle, Output[i] has to be inverted */
|
||||
/* and vol[i] incremented only if the exit status of the square */
|
||||
/* wave is 1. */
|
||||
while (R->Count[i] <= 0)
|
||||
{
|
||||
R->Count[i] += R->Period[i];
|
||||
if (R->Count[i] > 0)
|
||||
{
|
||||
R->Output[i] ^= 1;
|
||||
if (R->Output[i]) vol[i] += R->Period[i];
|
||||
break;
|
||||
}
|
||||
R->Count[i] += R->Period[i];
|
||||
vol[i] += R->Period[i];
|
||||
}
|
||||
if (R->Output[i]) vol[i] -= R->Count[i];
|
||||
}
|
||||
|
||||
left = STEP;
|
||||
do
|
||||
{
|
||||
int nextevent;
|
||||
|
||||
if (R->Count[3] < left) nextevent = R->Count[3];
|
||||
else nextevent = left;
|
||||
|
||||
if (R->Output[3]) vol[3] += R->Count[3];
|
||||
R->Count[3] -= nextevent;
|
||||
if (R->Count[3] <= 0)
|
||||
{
|
||||
if (R->RNG & 1) R->RNG ^= R->NoiseFB;
|
||||
R->RNG >>= 1;
|
||||
R->Output[3] = R->RNG & 1;
|
||||
R->Count[3] += R->Period[3];
|
||||
if (R->Output[3]) vol[3] += R->Period[3];
|
||||
}
|
||||
if (R->Output[3]) vol[3] -= R->Count[3];
|
||||
|
||||
left -= nextevent;
|
||||
} while (left > 0);
|
||||
|
||||
out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
|
||||
vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
|
||||
|
||||
if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;
|
||||
|
||||
if ((out /= STEP)) // will be optimized to shift; max 0x47ff = 18431
|
||||
*buffer += out;
|
||||
if(stereo) buffer+=2; // only left for stereo, to be mixed to right later
|
||||
else buffer++;
|
||||
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SN76496_set_clock(struct SN76496 *R,int clock)
|
||||
{
|
||||
|
||||
/* the base clock for the tone generators is the chip clock divided by 16; */
|
||||
/* for the noise generator, it is clock / 256. */
|
||||
/* Here we calculate the number of steps which happen during one sample */
|
||||
/* at the given sample rate. No. of events = sample rate / (clock/16). */
|
||||
/* STEP is a multiplier used to turn the fraction into a fixed point */
|
||||
/* number. */
|
||||
R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;
|
||||
}
|
||||
|
||||
|
||||
static void SN76496_set_gain(struct SN76496 *R,int gain)
|
||||
{
|
||||
int i;
|
||||
double out;
|
||||
|
||||
|
||||
gain &= 0xff;
|
||||
|
||||
/* increase max output basing on gain (0.2 dB per step) */
|
||||
out = MAX_OUTPUT / 3;
|
||||
while (gain-- > 0)
|
||||
out *= 1.023292992; /* = (10 ^ (0.2/20)) */
|
||||
|
||||
/* build volume table (2dB per step) */
|
||||
for (i = 0;i < 15;i++)
|
||||
{
|
||||
/* limit volume to avoid clipping */
|
||||
if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3;
|
||||
else R->VolTable[i] = out;
|
||||
|
||||
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
|
||||
}
|
||||
R->VolTable[15] = 0;
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
int SN76496_init(int clock,int sample_rate)
|
||||
{
|
||||
struct SN76496 *R = &ono_sn;
|
||||
int i;
|
||||
|
||||
//R->Channel = stream_create(0,1, sample_rate,R,SN76496Update);
|
||||
sn76496_regs = R->Register;
|
||||
|
||||
R->SampleRate = sample_rate;
|
||||
SN76496_set_clock(R,clock);
|
||||
|
||||
for (i = 0;i < 4;i++) R->Volume[i] = 0;
|
||||
|
||||
R->LastRegister = 0;
|
||||
for (i = 0;i < 8;i+=2)
|
||||
{
|
||||
R->Register[i] = 0;
|
||||
R->Register[i + 1] = 0x0f; /* volume = 0 */
|
||||
}
|
||||
|
||||
for (i = 0;i < 4;i++)
|
||||
{
|
||||
R->Output[i] = 0;
|
||||
R->Period[i] = R->Count[i] = R->UpdateStep;
|
||||
}
|
||||
R->RNG = NG_PRESET;
|
||||
R->Output[3] = R->RNG & 1;
|
||||
|
||||
// added
|
||||
SN76496_set_gain(R, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef SN76496_H
|
||||
#define SN76496_H
|
||||
|
||||
void SN76496Write(int data);
|
||||
void SN76496Update(short *buffer,int length,int stereo);
|
||||
int SN76496_init(int clock,int sample_rate);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2009
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ym2612.h"
|
||||
#include "sn76496.h"
|
||||
#include "../pico_int.h"
|
||||
#include "../cd/cue.h"
|
||||
#include "mix.h"
|
||||
|
||||
#define SIMPLE_WRITE_SOUND 0
|
||||
|
||||
void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_stereo;
|
||||
|
||||
// master int buffer to mix to
|
||||
static int PsndBuffer[2*(44100+100)/50];
|
||||
|
||||
// dac
|
||||
static unsigned short dac_info[312+4]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
|
||||
|
||||
// cdda output buffer
|
||||
short cdda_out_buffer[2*1152];
|
||||
|
||||
// for Pico
|
||||
int PsndRate=0;
|
||||
int PsndLen=0; // number of mono samples, multiply by 2 for stereo
|
||||
int PsndLen_exc_add=0; // this is for non-integer sample counts per line, eg. 22050/60
|
||||
int PsndLen_exc_cnt=0;
|
||||
int PsndDacLine=0;
|
||||
short *PsndOut=NULL; // PCM data buffer
|
||||
|
||||
// timers
|
||||
int timer_a_next_oflow, timer_a_step; // in z80 cycles
|
||||
int timer_b_next_oflow, timer_b_step;
|
||||
|
||||
// sn76496
|
||||
extern int *sn76496_regs;
|
||||
|
||||
|
||||
static void dac_recalculate(void)
|
||||
{
|
||||
int i, dac_cnt, pos, len, lines = Pico.m.pal ? 312 : 262, mid = Pico.m.pal ? 68 : 93;
|
||||
|
||||
if (PsndLen <= lines)
|
||||
{
|
||||
// shrinking algo
|
||||
dac_cnt = -PsndLen;
|
||||
len=1; pos=0;
|
||||
dac_info[225] = 1;
|
||||
|
||||
for(i=226; i != 225; i++)
|
||||
{
|
||||
if (i >= lines) i = 0;
|
||||
len = 0;
|
||||
if(dac_cnt < 0) {
|
||||
len=1;
|
||||
pos++;
|
||||
dac_cnt += lines;
|
||||
}
|
||||
dac_cnt -= PsndLen;
|
||||
dac_info[i] = (pos<<4)|len;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stretching
|
||||
dac_cnt = PsndLen;
|
||||
pos=0;
|
||||
for(i = 225; i != 224; i++)
|
||||
{
|
||||
if (i >= lines) i = 0;
|
||||
len=0;
|
||||
while(dac_cnt >= 0) {
|
||||
dac_cnt -= lines;
|
||||
len++;
|
||||
}
|
||||
if (i == mid) // midpoint
|
||||
while(pos+len < PsndLen/2) {
|
||||
dac_cnt -= lines;
|
||||
len++;
|
||||
}
|
||||
dac_cnt += PsndLen;
|
||||
dac_info[i] = (pos<<4)|len;
|
||||
pos+=len;
|
||||
}
|
||||
// last sample
|
||||
for(len = 0, i = pos; i < PsndLen; i++) len++;
|
||||
if (PsndLen_exc_add) len++;
|
||||
dac_info[224] = (pos<<4)|len;
|
||||
}
|
||||
mid = (dac_info[lines-1] & 0xfff0) + ((dac_info[lines-1] & 0xf) << 4);
|
||||
for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
|
||||
dac_info[i] = mid;
|
||||
//for(i=len=0; i < lines; i++) {
|
||||
// printf("%03i : %03i : %i\n", i, dac_info[i]>>4, dac_info[i]&0xf);
|
||||
// len+=dac_info[i]&0xf;
|
||||
//}
|
||||
//printf("rate is %i, len %f\n", PsndRate, (double)PsndRate/(Pico.m.pal ? 50.0 : 60.0));
|
||||
//printf("len total: %i, last pos: %i\n", len, pos);
|
||||
//exit(8);
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndReset(void)
|
||||
{
|
||||
// PsndRerate calls YM2612Init, which also resets
|
||||
PsndRerate(0);
|
||||
timers_reset();
|
||||
}
|
||||
|
||||
|
||||
// to be called after changing sound rate or chips
|
||||
void PsndRerate(int preserve_state)
|
||||
{
|
||||
void *state = NULL;
|
||||
int target_fps = Pico.m.pal ? 50 : 60;
|
||||
|
||||
if (preserve_state) {
|
||||
state = malloc(0x204);
|
||||
if (state == NULL) return;
|
||||
ym2612_pack_state();
|
||||
memcpy(state, YM2612GetRegs(), 0x204);
|
||||
}
|
||||
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
|
||||
if (preserve_state) {
|
||||
// feed it back it's own registers, just like after loading state
|
||||
memcpy(YM2612GetRegs(), state, 0x204);
|
||||
ym2612_unpack_state();
|
||||
}
|
||||
|
||||
if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state
|
||||
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
|
||||
if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state
|
||||
|
||||
if (state)
|
||||
free(state);
|
||||
|
||||
// calculate PsndLen
|
||||
PsndLen=PsndRate / target_fps;
|
||||
PsndLen_exc_add=((PsndRate - PsndLen*target_fps)<<16) / target_fps;
|
||||
PsndLen_exc_cnt=0;
|
||||
|
||||
// recalculate dac info
|
||||
dac_recalculate();
|
||||
|
||||
// clear all buffers
|
||||
memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);
|
||||
memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));
|
||||
if (PsndOut)
|
||||
PsndClear();
|
||||
|
||||
// set mixer
|
||||
PsndMix_32_to_16l = (PicoOpt & POPT_EN_STEREO) ? mix_32_to_16l_stereo : mix_32_to_16_mono;
|
||||
|
||||
if (PicoAHW & PAHW_PICO)
|
||||
PicoReratePico();
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndDoDAC(int line_to)
|
||||
{
|
||||
int pos, pos1, len;
|
||||
int dout = ym2612.dacout;
|
||||
int line_from = PsndDacLine;
|
||||
|
||||
if (line_to >= 312)
|
||||
line_to = 311;
|
||||
|
||||
PsndDacLine = line_to + 1;
|
||||
|
||||
pos =dac_info[line_from]>>4;
|
||||
pos1=dac_info[line_to];
|
||||
len = ((pos1>>4)-pos) + (pos1&0xf);
|
||||
if (!len) return;
|
||||
|
||||
if (PicoOpt & POPT_EN_STEREO) {
|
||||
short *d = PsndOut + pos*2;
|
||||
for (; len > 0; len--, d+=2) *d = dout;
|
||||
} else {
|
||||
short *d = PsndOut + pos;
|
||||
for (; len > 0; len--, d++) *d = dout;
|
||||
}
|
||||
}
|
||||
|
||||
// cdda
|
||||
static void cdda_raw_update(int *buffer, int length)
|
||||
{
|
||||
int ret, cdda_bytes, mult = 1;
|
||||
|
||||
cdda_bytes = length*4;
|
||||
if (PsndRate <= 22050 + 100) mult = 2;
|
||||
if (PsndRate < 22050 - 100) mult = 4;
|
||||
cdda_bytes *= mult;
|
||||
|
||||
ret = pm_read(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream);
|
||||
if (ret < cdda_bytes) {
|
||||
memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);
|
||||
Pico_mcd->cdda_stream = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// now mix
|
||||
switch (mult) {
|
||||
case 1: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;
|
||||
case 2: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;
|
||||
case 4: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;
|
||||
}
|
||||
}
|
||||
|
||||
void cdda_start_play(int lba_base, int lba_offset, int lb_len)
|
||||
{
|
||||
if (Pico_mcd->cdda_type == CT_MP3)
|
||||
{
|
||||
int pos1024 = 0;
|
||||
|
||||
if (lba_offset)
|
||||
pos1024 = lba_offset * 1024 / lb_len;
|
||||
|
||||
mp3_start_play(Pico_mcd->cdda_stream, pos1024);
|
||||
return;
|
||||
}
|
||||
|
||||
pm_seek(Pico_mcd->cdda_stream, (lba_base + lba_offset) * 2352, SEEK_SET);
|
||||
if (Pico_mcd->cdda_type == CT_WAV)
|
||||
{
|
||||
// skip headers, assume it's 44kHz stereo uncompressed
|
||||
pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndClear(void)
|
||||
{
|
||||
int len = PsndLen;
|
||||
if (PsndLen_exc_add) len++;
|
||||
if (PicoOpt & POPT_EN_STEREO)
|
||||
memset32((int *) PsndOut, 0, len); // assume PsndOut to be aligned
|
||||
else {
|
||||
short *out = PsndOut;
|
||||
if ((long)out & 2) { *out++ = 0; len--; }
|
||||
memset32((int *) out, 0, len/2);
|
||||
if (len & 1) out[len-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int PsndRender(int offset, int length)
|
||||
{
|
||||
int buf32_updated = 0;
|
||||
int *buf32 = PsndBuffer+offset;
|
||||
int stereo = (PicoOpt & 8) >> 3;
|
||||
|
||||
offset <<= stereo;
|
||||
|
||||
pprof_start(sound);
|
||||
|
||||
#if !SIMPLE_WRITE_SOUND
|
||||
if (offset == 0) { // should happen once per frame
|
||||
// compensate for float part of PsndLen
|
||||
PsndLen_exc_cnt += PsndLen_exc_add;
|
||||
if (PsndLen_exc_cnt >= 0x10000) {
|
||||
PsndLen_exc_cnt -= 0x10000;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// PSG
|
||||
if (PicoOpt & POPT_EN_PSG)
|
||||
SN76496Update(PsndOut+offset, length, stereo);
|
||||
|
||||
if (PicoAHW & PAHW_PICO) {
|
||||
PicoPicoPCMUpdate(PsndOut+offset, length, stereo);
|
||||
return length;
|
||||
}
|
||||
|
||||
// Add in the stereo FM buffer
|
||||
if (PicoOpt & POPT_EN_FM) {
|
||||
buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);
|
||||
} else
|
||||
memset32(buf32, 0, length<<stereo);
|
||||
|
||||
//printf("active_chs: %02x\n", buf32_updated);
|
||||
(void)buf32_updated;
|
||||
|
||||
// CD: PCM sound
|
||||
if (PicoAHW & PAHW_MCD) {
|
||||
pcd_pcm_update(buf32, length, stereo);
|
||||
//buf32_updated = 1;
|
||||
}
|
||||
|
||||
// CD: CDDA audio
|
||||
// CD mode, cdda enabled, not data track, CDC is reading
|
||||
if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA)
|
||||
&& Pico_mcd->cdda_stream != NULL
|
||||
&& !(Pico_mcd->s68k_regs[0x36] & 1))
|
||||
{
|
||||
// note: only 44, 22 and 11 kHz supported, with forced stereo
|
||||
if (Pico_mcd->cdda_type == CT_MP3)
|
||||
mp3_update(buf32, length, stereo);
|
||||
else
|
||||
cdda_raw_update(buf32, length);
|
||||
}
|
||||
|
||||
if ((PicoAHW & PAHW_32X) && (PicoOpt & POPT_EN_PWM))
|
||||
p32x_pwm_update(buf32, length, stereo);
|
||||
|
||||
// convert + limit to normal 16bit output
|
||||
PsndMix_32_to_16l(PsndOut+offset, buf32, length);
|
||||
|
||||
pprof_end(sound);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// to be called on 224 or line_sample scanlines only
|
||||
PICO_INTERNAL void PsndGetSamples(int y)
|
||||
{
|
||||
#if SIMPLE_WRITE_SOUND
|
||||
if (y != 224) return;
|
||||
PsndRender(0, PsndLen);
|
||||
if (PicoWriteSound)
|
||||
PicoWriteSound(PsndLen * ((PicoOpt & POPT_EN_STEREO) ? 4 : 2));
|
||||
PsndClear();
|
||||
#else
|
||||
static int curr_pos = 0;
|
||||
|
||||
if (y == 224)
|
||||
{
|
||||
if (emustatus & 2)
|
||||
curr_pos += PsndRender(curr_pos, PsndLen-PsndLen/2);
|
||||
else curr_pos = PsndRender(0, PsndLen);
|
||||
if (emustatus & 1)
|
||||
emustatus |= 2;
|
||||
else emustatus &= ~2;
|
||||
if (PicoWriteSound)
|
||||
PicoWriteSound(curr_pos * ((PicoOpt & POPT_EN_STEREO) ? 4 : 2));
|
||||
// clear sound buffer
|
||||
PsndClear();
|
||||
}
|
||||
else if (emustatus & 3) {
|
||||
emustatus|= 2;
|
||||
emustatus&=~1;
|
||||
curr_pos = PsndRender(0, PsndLen/2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PsndGetSamplesMS(void)
|
||||
{
|
||||
int stereo = (PicoOpt & 8) >> 3;
|
||||
int length = PsndLen;
|
||||
|
||||
#if !SIMPLE_WRITE_SOUND
|
||||
// compensate for float part of PsndLen
|
||||
PsndLen_exc_cnt += PsndLen_exc_add;
|
||||
if (PsndLen_exc_cnt >= 0x10000) {
|
||||
PsndLen_exc_cnt -= 0x10000;
|
||||
length++;
|
||||
}
|
||||
#endif
|
||||
|
||||
// PSG
|
||||
if (PicoOpt & POPT_EN_PSG)
|
||||
SN76496Update(PsndOut, length, stereo);
|
||||
|
||||
// upmix to "stereo" if needed
|
||||
if (stereo) {
|
||||
int i, *p;
|
||||
for (i = length, p = (void *)PsndOut; i > 0; i--, p++)
|
||||
*p |= *p << 16;
|
||||
}
|
||||
|
||||
if (PicoWriteSound != NULL)
|
||||
PicoWriteSound(length * ((PicoOpt & POPT_EN_STEREO) ? 4 : 2));
|
||||
PsndClear();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
header file for software emulation for FM sound generator
|
||||
|
||||
*/
|
||||
#ifndef _H_FM_FM_
|
||||
#define _H_FM_FM_
|
||||
|
||||
/* compiler dependence */
|
||||
#ifndef UINT8
|
||||
typedef unsigned char UINT8; /* unsigned 8bit */
|
||||
typedef unsigned short UINT16; /* unsigned 16bit */
|
||||
typedef unsigned int UINT32; /* unsigned 32bit */
|
||||
#endif
|
||||
#ifndef INT8
|
||||
typedef signed char INT8; /* signed 8bit */
|
||||
typedef signed short INT16; /* signed 16bit */
|
||||
typedef signed int INT32; /* signed 32bit */
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
/* struct describing a single operator (SLOT) */
|
||||
typedef struct
|
||||
{
|
||||
INT32 *DT; /* #0x00 detune :dt_tab[DT] */
|
||||
UINT8 ar; /* #0x04 attack rate */
|
||||
UINT8 d1r; /* #0x05 decay rate */
|
||||
UINT8 d2r; /* #0x06 sustain rate */
|
||||
UINT8 rr; /* #0x07 release rate */
|
||||
UINT32 mul; /* #0x08 multiple :ML_TABLE[ML] */
|
||||
|
||||
/* Phase Generator */
|
||||
UINT32 phase; /* #0x0c phase counter | need_save */
|
||||
UINT32 Incr; /* #0x10 phase step */
|
||||
|
||||
UINT8 KSR; /* #0x14 key scale rate :3-KSR */
|
||||
UINT8 ksr; /* #0x15 key scale rate :kcode>>(3-KSR) */
|
||||
|
||||
UINT8 key; /* #0x16 0=last key was KEY OFF, 1=KEY ON */
|
||||
|
||||
/* Envelope Generator */
|
||||
UINT8 state; /* #0x17 phase type: EG_OFF=0, EG_REL, EG_SUS, EG_DEC, EG_ATT | need_save */
|
||||
UINT16 tl; /* #0x18 total level: TL << 3 */
|
||||
INT16 volume; /* #0x1a envelope counter | need_save */
|
||||
UINT32 sl; /* #0x1c sustain level:sl_table[SL] */
|
||||
|
||||
UINT32 eg_pack_ar; /* #0x20 (attack state) */
|
||||
UINT32 eg_pack_d1r; /* #0x24 (decay state) */
|
||||
UINT32 eg_pack_d2r; /* #0x28 (sustain state) */
|
||||
UINT32 eg_pack_rr; /* #0x2c (release state) */
|
||||
} FM_SLOT;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FM_SLOT SLOT[4]; /* four SLOTs (operators) */
|
||||
|
||||
UINT8 ALGO; /* +00 algorithm */
|
||||
UINT8 FB; /* feedback shift */
|
||||
UINT8 pad[2];
|
||||
INT32 op1_out; /* op1 output for feedback */
|
||||
|
||||
INT32 mem_value; /* +08 delayed sample (MEM) value */
|
||||
|
||||
INT32 pms; /* channel PMS */
|
||||
UINT8 ams; /* channel AMS */
|
||||
|
||||
UINT8 kcode; /* +11 key code: */
|
||||
UINT8 fn_h; /* freq latch */
|
||||
UINT8 pad2;
|
||||
UINT32 fc; /* fnum,blk:adjusted to sample rate */
|
||||
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
|
||||
|
||||
/* LFO */
|
||||
UINT8 AMmasks; /* AM enable flag */
|
||||
UINT8 pad3[3];
|
||||
} FM_CH;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int clock; /* master clock (Hz) */
|
||||
int rate; /* sampling rate (Hz) */
|
||||
double freqbase; /* 08 frequency base */
|
||||
UINT8 address; /* 10 address register | need_save */
|
||||
UINT8 status; /* 11 status flag | need_save */
|
||||
UINT8 mode; /* mode CSM / 3SLOT */
|
||||
UINT8 pad;
|
||||
int TA; /* timer a */
|
||||
int TAC; /* timer a maxval */
|
||||
int TAT; /* timer a ticker | need_save */
|
||||
UINT8 TB; /* timer b */
|
||||
UINT8 pad2[3];
|
||||
int TBC; /* timer b maxval */
|
||||
int TBT; /* timer b ticker | need_save */
|
||||
/* local time tables */
|
||||
INT32 dt_tab[8][32];/* DeTune table */
|
||||
} FM_ST;
|
||||
|
||||
/***********************************************************/
|
||||
/* OPN unit */
|
||||
/***********************************************************/
|
||||
|
||||
/* OPN 3slot struct */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 fc[3]; /* fnum3,blk3: calculated */
|
||||
UINT8 fn_h; /* freq3 latch */
|
||||
UINT8 kcode[3]; /* key code */
|
||||
UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
|
||||
} FM_3SLOT;
|
||||
|
||||
/* OPN/A/B common state */
|
||||
typedef struct
|
||||
{
|
||||
FM_ST ST; /* general state */
|
||||
FM_3SLOT SL3; /* 3 slot mode state */
|
||||
UINT32 pan; /* fm channels output mask (bit 1 = enable) */
|
||||
|
||||
UINT32 eg_cnt; /* global envelope generator counter | need_save */
|
||||
UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 | need_save */
|
||||
UINT32 eg_timer_add; /* step of eg_timer */
|
||||
|
||||
/* LFO */
|
||||
UINT32 lfo_cnt; /* need_save */
|
||||
UINT32 lfo_inc;
|
||||
|
||||
UINT32 lfo_freq[8]; /* LFO FREQ table */
|
||||
} FM_OPN;
|
||||
|
||||
/* here's the virtual YM2612 */
|
||||
typedef struct
|
||||
{
|
||||
UINT8 REGS[0x200]; /* registers (for save states) */
|
||||
INT32 addr_A1; /* address line A1 | need_save */
|
||||
|
||||
FM_CH CH[6]; /* channel state */
|
||||
|
||||
/* dac output (YM2612) */
|
||||
int dacen;
|
||||
INT32 dacout;
|
||||
|
||||
FM_OPN OPN; /* OPN state */
|
||||
|
||||
UINT32 slot_mask; /* active slot mask (performance hack) */
|
||||
} YM2612;
|
||||
#endif
|
||||
|
||||
#ifndef EXTERNAL_YM2612
|
||||
extern YM2612 ym2612;
|
||||
#endif
|
||||
|
||||
void YM2612Init_(int baseclock, int rate);
|
||||
void YM2612ResetChip_(void);
|
||||
int YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty);
|
||||
|
||||
int YM2612Write_(unsigned int a, unsigned int v);
|
||||
//unsigned char YM2612Read_(void);
|
||||
|
||||
int YM2612PicoTick_(int n);
|
||||
void YM2612PicoStateLoad_(void);
|
||||
|
||||
void *YM2612GetRegs(void);
|
||||
void YM2612PicoStateSave2(int tat, int tbt);
|
||||
int YM2612PicoStateLoad2(int *tat, int *tbt);
|
||||
|
||||
#ifndef __GP2X__
|
||||
#define YM2612Init YM2612Init_
|
||||
#define YM2612ResetChip YM2612ResetChip_
|
||||
#define YM2612UpdateOne YM2612UpdateOne_
|
||||
#define YM2612PicoStateLoad YM2612PicoStateLoad_
|
||||
#else
|
||||
/* GP2X specific */
|
||||
#include "../../platform/gp2x/940ctl.h"
|
||||
extern int PicoOpt;
|
||||
#define YM2612Init(baseclock,rate) { \
|
||||
if (PicoOpt&0x200) YM2612Init_940(baseclock, rate); \
|
||||
else YM2612Init_(baseclock, rate); \
|
||||
}
|
||||
#define YM2612ResetChip() { \
|
||||
if (PicoOpt&0x200) YM2612ResetChip_940(); \
|
||||
else YM2612ResetChip_(); \
|
||||
}
|
||||
#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) \
|
||||
(PicoOpt&0x200) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \
|
||||
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty);
|
||||
#define YM2612PicoStateLoad() { \
|
||||
if (PicoOpt&0x200) YM2612PicoStateLoad_940(); \
|
||||
else YM2612PicoStateLoad_(); \
|
||||
}
|
||||
#endif /* __GP2X__ */
|
||||
|
||||
|
||||
#endif /* _H_FM_FM_ */
|
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (c) Copyright Dave, 2004
|
||||
* (C) notaz, 2006-2009
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "pico_int.h"
|
||||
|
||||
int line_base_cycles;
|
||||
extern const unsigned char hcounts_32[];
|
||||
extern const unsigned char hcounts_40[];
|
||||
|
||||
#ifndef UTYPES_DEFINED
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
#define UTYPES_DEFINED
|
||||
#endif
|
||||
|
||||
int (*PicoDmaHook)(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp) = NULL;
|
||||
|
||||
static __inline void AutoIncrement(void)
|
||||
{
|
||||
Pico.video.addr=(unsigned short)(Pico.video.addr+Pico.video.reg[0xf]);
|
||||
}
|
||||
|
||||
static void VideoWrite(u16 d)
|
||||
{
|
||||
unsigned int a=Pico.video.addr;
|
||||
|
||||
switch (Pico.video.type)
|
||||
{
|
||||
case 1: if(a&1) d=(u16)((d<<8)|(d>>8)); // If address is odd, bytes are swapped (which game needs this?)
|
||||
Pico.vram [(a>>1)&0x7fff]=d;
|
||||
if (a - ((unsigned)(Pico.video.reg[5]&0x7f) << 9) < 0x400)
|
||||
rendstatus |= PDRAW_DIRTY_SPRITES;
|
||||
break;
|
||||
case 3: Pico.m.dirtyPal = 1;
|
||||
Pico.cram [(a>>1)&0x003f]=d; break; // wraps (Desert Strike)
|
||||
case 5: Pico.vsram[(a>>1)&0x003f]=d; break;
|
||||
//default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, Pico.video.type); break;
|
||||
}
|
||||
|
||||
AutoIncrement();
|
||||
}
|
||||
|
||||
static unsigned int VideoRead(void)
|
||||
{
|
||||
unsigned int a=0,d=0;
|
||||
|
||||
a=Pico.video.addr; a>>=1;
|
||||
|
||||
switch (Pico.video.type)
|
||||
{
|
||||
case 0: d=Pico.vram [a&0x7fff]; break;
|
||||
case 8: d=Pico.cram [a&0x003f]; break;
|
||||
case 4: d=Pico.vsram[a&0x003f]; break;
|
||||
default:elprintf(EL_ANOMALY, "VDP read with bad type %i", Pico.video.type); break;
|
||||
}
|
||||
|
||||
AutoIncrement();
|
||||
return d;
|
||||
}
|
||||
|
||||
static int GetDmaLength(void)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
int len=0;
|
||||
// 16-bit words to transfer:
|
||||
len =pvid->reg[0x13];
|
||||
len|=pvid->reg[0x14]<<8;
|
||||
// Charles MacDonald:
|
||||
if(!len) len = 0xffff;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void DmaSlow(int len)
|
||||
{
|
||||
u16 *pd=0, *pdend, *r;
|
||||
unsigned int a=Pico.video.addr, a2, d;
|
||||
unsigned char inc=Pico.video.reg[0xf];
|
||||
unsigned int source;
|
||||
|
||||
source =Pico.video.reg[0x15]<<1;
|
||||
source|=Pico.video.reg[0x16]<<9;
|
||||
source|=Pico.video.reg[0x17]<<17;
|
||||
|
||||
elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%i] @ %06x",
|
||||
Pico.video.type, source, a, len, inc, (Pico.video.status&8)||!(Pico.video.reg[1]&0x40),
|
||||
SekCyclesDone(), SekPc);
|
||||
|
||||
Pico.m.dma_xfers += len;
|
||||
SekCyclesBurnRun(CheckDMA());
|
||||
|
||||
if ((source&0xe00000)==0xe00000) { // Ram
|
||||
pd=(u16 *)(Pico.ram+(source&0xfffe));
|
||||
pdend=(u16 *)(Pico.ram+0x10000);
|
||||
}
|
||||
else if (PicoAHW & PAHW_MCD)
|
||||
{
|
||||
elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", Pico_mcd->s68k_regs[3]);
|
||||
if(source<0x20000) { // Bios area
|
||||
pd=(u16 *)(Pico_mcd->bios+(source&~1));
|
||||
pdend=(u16 *)(Pico_mcd->bios+0x20000);
|
||||
} else if ((source&0xfc0000)==0x200000) { // Word Ram
|
||||
source -= 2;
|
||||
if (!(Pico_mcd->s68k_regs[3]&4)) { // 2M mode
|
||||
pd=(u16 *)(Pico_mcd->word_ram2M+(source&0x3fffe));
|
||||
pdend=(u16 *)(Pico_mcd->word_ram2M+0x40000);
|
||||
} else {
|
||||
if (source < 0x220000) { // 1M mode
|
||||
int bank = Pico_mcd->s68k_regs[3]&1;
|
||||
pd=(u16 *)(Pico_mcd->word_ram1M[bank]+(source&0x1fffe));
|
||||
pdend=(u16 *)(Pico_mcd->word_ram1M[bank]+0x20000);
|
||||
} else {
|
||||
DmaSlowCell(source, a, len, inc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if ((source&0xfe0000)==0x020000) { // Prg Ram
|
||||
u8 *prg_ram = Pico_mcd->prg_ram_b[Pico_mcd->s68k_regs[3]>>6];
|
||||
pd=(u16 *)(prg_ram+(source&0x1fffe));
|
||||
pdend=(u16 *)(prg_ram+0x20000);
|
||||
} else {
|
||||
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: FIXME: unsupported src", Pico.video.type, source, a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we have DmaHook, let it handle ROM because of possible DMA delay
|
||||
if (PicoDmaHook && PicoDmaHook(source, len, &pd, &pdend));
|
||||
else if (source<Pico.romsize) { // Rom
|
||||
pd=(u16 *)(Pico.rom+(source&~1));
|
||||
pdend=(u16 *)(Pico.rom+Pico.romsize);
|
||||
}
|
||||
else {
|
||||
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", Pico.video.type, source, a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// overflow protection, might break something..
|
||||
if (len > pdend - pd) {
|
||||
len = pdend - pd;
|
||||
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow overflow");
|
||||
}
|
||||
|
||||
switch (Pico.video.type)
|
||||
{
|
||||
case 1: // vram
|
||||
r = Pico.vram;
|
||||
if (inc == 2 && !(a&1) && a+len*2 < 0x10000)
|
||||
{
|
||||
// most used DMA mode
|
||||
memcpy16(r + (a>>1), pd, len);
|
||||
a += len*2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(; len; len--)
|
||||
{
|
||||
d=*pd++;
|
||||
if(a&1) d=(d<<8)|(d>>8);
|
||||
r[a>>1] = (u16)d; // will drop the upper bits
|
||||
// AutoIncrement
|
||||
a=(u16)(a+inc);
|
||||
// didn't src overlap?
|
||||
//if(pd >= pdend) pd-=0x8000; // should be good for RAM, bad for ROM
|
||||
}
|
||||
}
|
||||
rendstatus |= PDRAW_DIRTY_SPRITES;
|
||||
break;
|
||||
|
||||
case 3: // cram
|
||||
Pico.m.dirtyPal = 1;
|
||||
r = Pico.cram;
|
||||
for(a2=a&0x7f; len; len--)
|
||||
{
|
||||
r[a2>>1] = (u16)*pd++; // bit 0 is ignored
|
||||
// AutoIncrement
|
||||
a2+=inc;
|
||||
// didn't src overlap?
|
||||
//if(pd >= pdend) pd-=0x8000;
|
||||
// good dest?
|
||||
if(a2 >= 0x80) break; // Todds Adventures in Slime World / Andre Agassi tennis
|
||||
}
|
||||
a=(a&0xff00)|a2;
|
||||
break;
|
||||
|
||||
case 5: // vsram[a&0x003f]=d;
|
||||
r = Pico.vsram;
|
||||
for(a2=a&0x7f; len; len--)
|
||||
{
|
||||
r[a2>>1] = (u16)*pd++;
|
||||
// AutoIncrement
|
||||
a2+=inc;
|
||||
// didn't src overlap?
|
||||
//if(pd >= pdend) pd-=0x8000;
|
||||
// good dest?
|
||||
if(a2 >= 0x80) break;
|
||||
}
|
||||
a=(a&0xff00)|a2;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Pico.video.type != 0 || (EL_LOGMASK & EL_VDPDMA))
|
||||
elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", Pico.video.type);
|
||||
break;
|
||||
}
|
||||
// remember addr
|
||||
Pico.video.addr=(u16)a;
|
||||
}
|
||||
|
||||
static void DmaCopy(int len)
|
||||
{
|
||||
u16 a=Pico.video.addr;
|
||||
unsigned char *vr = (unsigned char *) Pico.vram;
|
||||
unsigned char *vrs;
|
||||
unsigned char inc=Pico.video.reg[0xf];
|
||||
int source;
|
||||
elprintf(EL_VDPDMA, "DmaCopy len %i [%i]", len, SekCyclesDone());
|
||||
|
||||
Pico.m.dma_xfers += len;
|
||||
Pico.video.status |= 2; // dma busy
|
||||
|
||||
source =Pico.video.reg[0x15];
|
||||
source|=Pico.video.reg[0x16]<<8;
|
||||
vrs=vr+source;
|
||||
|
||||
if (source+len > 0x10000) len=0x10000-source; // clip??
|
||||
|
||||
for (; len; len--)
|
||||
{
|
||||
vr[a] = *vrs++;
|
||||
// AutoIncrement
|
||||
a=(u16)(a+inc);
|
||||
}
|
||||
// remember addr
|
||||
Pico.video.addr=a;
|
||||
rendstatus |= PDRAW_DIRTY_SPRITES;
|
||||
}
|
||||
|
||||
// check: Contra, Megaman
|
||||
// note: this is still inaccurate
|
||||
static void DmaFill(int data)
|
||||
{
|
||||
int len;
|
||||
unsigned short a=Pico.video.addr;
|
||||
unsigned char *vr=(unsigned char *) Pico.vram;
|
||||
unsigned char high = (unsigned char) (data >> 8);
|
||||
unsigned char inc=Pico.video.reg[0xf];
|
||||
|
||||
len=GetDmaLength();
|
||||
elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%i]", len, inc, SekCyclesDone());
|
||||
|
||||
Pico.m.dma_xfers += len;
|
||||
Pico.video.status |= 2; // dma busy
|
||||
|
||||
// from Charles MacDonald's genvdp.txt:
|
||||
// Write lower byte to address specified
|
||||
vr[a] = (unsigned char) data;
|
||||
a=(u16)(a+inc);
|
||||
|
||||
if (!inc) len=1;
|
||||
|
||||
for (; len; len--) {
|
||||
// Write upper byte to adjacent address
|
||||
// (here we are byteswapped, so address is already 'adjacent')
|
||||
vr[a] = high;
|
||||
|
||||
// Increment address register
|
||||
a=(u16)(a+inc);
|
||||
}
|
||||
// remember addr
|
||||
Pico.video.addr=a;
|
||||
// update length
|
||||
Pico.video.reg[0x13] = Pico.video.reg[0x14] = 0; // Dino Dini's Soccer (E) (by Haze)
|
||||
|
||||
rendstatus |= PDRAW_DIRTY_SPRITES;
|
||||
}
|
||||
|
||||
static void CommandDma(void)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
int len=0,method=0;
|
||||
|
||||
if ((pvid->reg[1]&0x10)==0) return; // DMA not enabled
|
||||
|
||||
len=GetDmaLength();
|
||||
|
||||
method=pvid->reg[0x17]>>6;
|
||||
if (method< 2) DmaSlow(len); // 68000 to VDP
|
||||
if (method==3) DmaCopy(len); // VRAM Copy
|
||||
}
|
||||
|
||||
static void CommandChange(void)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
unsigned int cmd=0,addr=0;
|
||||
|
||||
cmd=pvid->command;
|
||||
|
||||
// Get type of transfer 0xc0000030 (v/c/vsram read/write)
|
||||
pvid->type=(unsigned char)(((cmd>>2)&0xc)|(cmd>>30));
|
||||
|
||||
// Get address 0x3fff0003
|
||||
addr =(cmd>>16)&0x3fff;
|
||||
addr|=(cmd<<14)&0xc000;
|
||||
pvid->addr=(unsigned short)addr;
|
||||
|
||||
// Check for dma:
|
||||
if (cmd&0x80) CommandDma();
|
||||
}
|
||||
|
||||
static void DrawSync(int blank_on)
|
||||
{
|
||||
if (Pico.m.scanline < 224 && !(PicoOpt & POPT_ALT_RENDERER) &&
|
||||
!PicoSkipFrame && DrawScanline <= Pico.m.scanline) {
|
||||
//elprintf(EL_ANOMALY, "sync");
|
||||
PicoDrawSync(Pico.m.scanline, blank_on);
|
||||
}
|
||||
}
|
||||
|
||||
PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d)
|
||||
{
|
||||
struct PicoVideo *pvid=&Pico.video;
|
||||
|
||||
//if (Pico.m.scanline < 224)
|
||||
// elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x", a, d);
|
||||
a&=0x1c;
|
||||
|
||||
if (a==0x00) // Data port 0 or 2
|
||||
{
|
||||
// try avoiding the sync..
|
||||
if (Pico.m.scanline < 224 && (pvid->reg[1]&0x40) &&
|
||||
!(!pvid->pending &&
|
||||
((pvid->command & 0xc00000f0) == 0x40000010 && Pico.vsram[pvid->addr>>1] == d))
|
||||
)
|
||||
DrawSync(0);
|
||||
|
||||
if (pvid->pending) {
|
||||
CommandChange();
|
||||
pvid->pending=0;
|
||||
}
|
||||
|
||||
// If a DMA fill has been set up, do it
|
||||
if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[0x17]>>6)==2)
|
||||
{
|
||||
DmaFill(d);
|
||||
}
|
||||
else
|
||||
{
|
||||
// preliminary FIFO emulation for Chaos Engine, The (E)
|
||||
if (!(pvid->status&8) && (pvid->reg[1]&0x40) && !(PicoOpt&POPT_DIS_VDP_FIFO)) // active display?
|
||||
{
|
||||
pvid->status&=~0x200; // FIFO no longer empty
|
||||
pvid->lwrite_cnt++;
|
||||
if (pvid->lwrite_cnt >= 4) pvid->status|=0x100; // FIFO full
|
||||
if (pvid->lwrite_cnt > 4) {
|
||||
SekCyclesBurnRun(32); // penalty // 488/12-8
|
||||
}
|
||||
elprintf(EL_ASVDP, "VDP data write: %04x [%06x] {%i} #%i @ %06x", d, Pico.video.addr,
|
||||
Pico.video.type, pvid->lwrite_cnt, SekPc);
|
||||
}
|
||||
VideoWrite(d);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (a==0x04) // Control (command) port 4 or 6
|
||||
{
|
||||
if (pvid->pending)
|
||||
{
|
||||
if (d & 0x80) DrawSync(0); // only need sync for DMA
|
||||
// Low word of command:
|
||||
pvid->command&=0xffff0000;
|
||||
pvid->command|=d;
|
||||
pvid->pending=0;
|
||||
CommandChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((d&0xc000)==0x8000)
|
||||
{
|
||||
// Register write:
|
||||
int num=(d>>8)&0x1f;
|
||||
int dold=pvid->reg[num];
|
||||
int blank_on = 0;
|
||||
pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)
|
||||
if (num > 0x0a && !(pvid->reg[1]&4)) {
|
||||
elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (num == 1 && !(d&0x40) && SekCyclesDone() - line_base_cycles <= 488-390)
|
||||
blank_on = 1;
|
||||
DrawSync(blank_on);
|
||||
pvid->reg[num]=(unsigned char)d;
|
||||
switch (num)
|
||||
{
|
||||
case 0x00:
|
||||
elprintf(EL_INTSW, "hint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x10)>>4,
|
||||
(d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);
|
||||
goto update_irq;
|
||||
case 0x01:
|
||||
elprintf(EL_INTSW, "vint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x20)>>5,
|
||||
(d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);
|
||||
goto update_irq;
|
||||
case 0x05:
|
||||
//elprintf(EL_STATUS, "spritep moved to %04x", (unsigned)(Pico.video.reg[5]&0x7f) << 9);
|
||||
if (d^dold) rendstatus |= PDRAW_SPRITES_MOVED;
|
||||
break;
|
||||
case 0x0c:
|
||||
// renderers should update their palettes if sh/hi mode is changed
|
||||
if ((d^dold)&8) Pico.m.dirtyPal = 2;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
update_irq:
|
||||
#ifndef EMU_CORE_DEBUG
|
||||
// update IRQ level
|
||||
if (!SekShouldInterrupt()) // hack
|
||||
{
|
||||
int lines, pints, irq=0;
|
||||
lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);
|
||||
pints = (pvid->pending_ints&lines);
|
||||
if (pints & 0x20) irq = 6;
|
||||
else if (pints & 0x10) irq = 4;
|
||||
SekInterrupt(irq); // update line
|
||||
|
||||
if (irq) SekEndRun(24); // make it delayed
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// High word of command:
|
||||
pvid->command&=0x0000ffff;
|
||||
pvid->command|=d<<16;
|
||||
pvid->pending=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PICO_INTERNAL_ASM unsigned int PicoVideoRead(unsigned int a)
|
||||
{
|
||||
a&=0x1c;
|
||||
|
||||
if (a==0x04) // control port
|
||||
{
|
||||
struct PicoVideo *pv=&Pico.video;
|
||||
unsigned int d;
|
||||
d=pv->status;
|
||||
//if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)
|
||||
if (SekCyclesDone() - line_base_cycles >= 488-88)
|
||||
d|=0x0004; // H-Blank (Sonic3 vs)
|
||||
|
||||
d |= ((pv->reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled
|
||||
d |= (pv->pending_ints&0x20)<<2; // V-int pending?
|
||||
if (d&0x100) pv->status&=~0x100; // FIFO no longer full
|
||||
|
||||
pv->pending = 0; // ctrl port reads clear write-pending flag (Charles MacDonald)
|
||||
|
||||
elprintf(EL_SR, "SR read: %04x @ %06x", d, SekPc);
|
||||
return d;
|
||||
}
|
||||
|
||||
// H-counter info (based on Generator):
|
||||
// frame:
|
||||
// | <- hblank? -> |
|
||||
// start <416> hint <36> hdisplay <38> end // CPU cycles
|
||||
// |---------...---------|------------|-------------|
|
||||
// 0 B6 E4 FF // 40 cells
|
||||
// 0 93 E8 FF // 32 cells
|
||||
|
||||
// Gens (?) v-render
|
||||
// start <hblank=84> hint hdisplay <404> |
|
||||
// |---------------------|--------------------------|
|
||||
// E4 (hc[0x43]==0) 07 B1 // 40
|
||||
// E8 (hc[0x45]==0) 05 91 // 32
|
||||
|
||||
// check: Sonic 3D Blast bonus, Cannon Fodder, Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune
|
||||
if ((a&0x1c)==0x08)
|
||||
{
|
||||
unsigned int d;
|
||||
|
||||
d = (SekCyclesDone() - line_base_cycles) & 0x1ff; // FIXME
|
||||
if (Pico.video.reg[12]&1)
|
||||
d = hcounts_40[d];
|
||||
else d = hcounts_32[d];
|
||||
|
||||
elprintf(EL_HVCNT, "hv: %02x %02x (%i) @ %06x", d, Pico.video.v_counter, SekCyclesDone(), SekPc);
|
||||
return d | (Pico.video.v_counter << 8);
|
||||
}
|
||||
|
||||
if (a==0x00) // data port
|
||||
{
|
||||
return VideoRead();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int PicoVideoRead8(unsigned int a)
|
||||
{
|
||||
unsigned int d;
|
||||
a&=0x1d;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0: return VideoRead() >> 8;
|
||||
case 1: return VideoRead() & 0xff;
|
||||
case 4: // control port/status reg
|
||||
d = Pico.video.status >> 8;
|
||||
if (d&1) Pico.video.status&=~0x100; // FIFO no longer full
|
||||
Pico.video.pending = 0;
|
||||
elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);
|
||||
return d;
|
||||
case 5:
|
||||
d = Pico.video.status & 0xff;
|
||||
//if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)
|
||||
d |= ((Pico.video.reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled
|
||||
d |= (Pico.video.pending_ints&0x20)<<2; // V-int pending?
|
||||
if (SekCyclesDone() - line_base_cycles >= 488-88) d |= 4; // H-Blank
|
||||
Pico.video.pending = 0;
|
||||
elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);
|
||||
return d;
|
||||
case 8: // hv counter
|
||||
elprintf(EL_HVCNT, "vcounter: %02x (%i) @ %06x", Pico.video.v_counter, SekCyclesDone(), SekPc);
|
||||
return Pico.video.v_counter;
|
||||
case 9:
|
||||
d = (SekCyclesDone() - line_base_cycles) & 0x1ff; // FIXME
|
||||
if (Pico.video.reg[12]&1)
|
||||
d = hcounts_40[d];
|
||||
else d = hcounts_32[d];
|
||||
elprintf(EL_HVCNT, "hcounter: %02x (%i) @ %06x", d, SekCyclesDone(), SekPc);
|
||||
return d;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2007-2010
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "pico_int.h"
|
||||
#include "memory.h"
|
||||
|
||||
uptr z80_read_map [0x10000 >> Z80_MEM_SHIFT];
|
||||
uptr z80_write_map[0x10000 >> Z80_MEM_SHIFT];
|
||||
|
||||
#ifdef _USE_DRZ80
|
||||
struct DrZ80 drZ80;
|
||||
|
||||
static u32 drz80_sp_base;
|
||||
|
||||
static void drz80_load_pcsp(u32 pc, u32 sp)
|
||||
{
|
||||
drZ80.Z80PC_BASE = z80_read_map[pc >> Z80_MEM_SHIFT];
|
||||
if (drZ80.Z80PC_BASE & (1<<31)) {
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad PC: %04x", pc);
|
||||
drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0];
|
||||
} else {
|
||||
drZ80.Z80PC_BASE <<= 1;
|
||||
drZ80.Z80PC = drZ80.Z80PC_BASE + pc;
|
||||
}
|
||||
drZ80.Z80SP_BASE = z80_read_map[sp >> Z80_MEM_SHIFT];
|
||||
if (drZ80.Z80SP_BASE & (1<<31)) {
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad SP: %04x", sp);
|
||||
drZ80.Z80SP_BASE = z80_read_map[0];
|
||||
drZ80.Z80SP = drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT);
|
||||
} else {
|
||||
drZ80.Z80SP_BASE <<= 1;
|
||||
drZ80.Z80SP = drZ80.Z80SP_BASE + sp;
|
||||
}
|
||||
}
|
||||
|
||||
// called only if internal xmap rebase fails
|
||||
static unsigned int dz80_rebase_pc(unsigned short pc)
|
||||
{
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_pc: fail on %04x", pc);
|
||||
drZ80.Z80PC_BASE = z80_read_map[0] << 1;
|
||||
return drZ80.Z80PC_BASE;
|
||||
}
|
||||
|
||||
static unsigned int dz80_rebase_sp(unsigned short sp)
|
||||
{
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_sp: fail on %04x", sp);
|
||||
drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
|
||||
return drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT) - 0x100;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void z80_init(void)
|
||||
{
|
||||
#ifdef _USE_DRZ80
|
||||
memset(&drZ80, 0, sizeof(drZ80));
|
||||
drZ80.z80_rebasePC = dz80_rebase_pc;
|
||||
drZ80.z80_rebaseSP = dz80_rebase_sp;
|
||||
drZ80.z80_read8 = (void *)z80_read_map;
|
||||
drZ80.z80_read16 = NULL;
|
||||
drZ80.z80_write8 = (void *)z80_write_map;
|
||||
drZ80.z80_write16 = NULL;
|
||||
drZ80.z80_irq_callback = NULL;
|
||||
#endif
|
||||
#ifdef _USE_CZ80
|
||||
memset(&CZ80, 0, sizeof(CZ80));
|
||||
Cz80_Init(&CZ80);
|
||||
Cz80_Set_ReadB(&CZ80, NULL); // unused (hacked in)
|
||||
Cz80_Set_WriteB(&CZ80, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void z80_reset(void)
|
||||
{
|
||||
#ifdef _USE_DRZ80
|
||||
drZ80.Z80I = 0;
|
||||
drZ80.Z80IM = 0;
|
||||
drZ80.Z80IF = 0;
|
||||
drZ80.z80irqvector = 0xff0000; // RST 38h
|
||||
drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0] << 1;
|
||||
// others not changed, undefined on cold boot
|
||||
/*
|
||||
drZ80.Z80F = (1<<2); // set ZFlag
|
||||
drZ80.Z80F2 = (1<<2); // set ZFlag
|
||||
drZ80.Z80IX = 0xFFFF << 16;
|
||||
drZ80.Z80IY = 0xFFFF << 16;
|
||||
*/
|
||||
// drZ80 is locked in single bank
|
||||
drz80_sp_base = (PicoAHW & PAHW_SMS) ? 0xc000 : 0x0000;
|
||||
drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
|
||||
if (PicoAHW & PAHW_SMS)
|
||||
drZ80.Z80SP = drZ80.Z80SP_BASE + 0xdff0; // simulate BIOS
|
||||
// XXX: since we use direct SP pointer, it might make sense to force it to RAM,
|
||||
// but we'll rely on built-in stack protection for now
|
||||
#endif
|
||||
#ifdef _USE_CZ80
|
||||
Cz80_Reset(&CZ80);
|
||||
if (PicoAHW & PAHW_SMS)
|
||||
Cz80_Set_Reg(&CZ80, CZ80_SP, 0xdff0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* save state stuff */
|
||||
static int z80_unpack_legacy(const void *data)
|
||||
{
|
||||
#if defined(_USE_DRZ80)
|
||||
if (*(int *)data == 0x015A7244) { // "DrZ" v1 save?
|
||||
u32 pc, sp;
|
||||
memcpy(&drZ80, data+4, 0x54);
|
||||
pc = (drZ80.Z80PC - drZ80.Z80PC_BASE) & 0xffff;
|
||||
sp = (drZ80.Z80SP - drZ80.Z80SP_BASE) & 0xffff;
|
||||
// update bases
|
||||
drz80_load_pcsp(pc, sp);
|
||||
return 0;
|
||||
}
|
||||
#elif defined(_USE_CZ80)
|
||||
if (*(int *)data == 0x00007a43) { // "Cz" save?
|
||||
memcpy(&CZ80, data+8, offsetof(cz80_struc, BasePC));
|
||||
Cz80_Set_Reg(&CZ80, CZ80_PC, *(int *)(data+4));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct z80sr_main {
|
||||
u8 a, f;
|
||||
u8 b, c;
|
||||
u8 d, e;
|
||||
u8 h, l;
|
||||
};
|
||||
|
||||
struct z80_state {
|
||||
char magic[4];
|
||||
// regs
|
||||
struct z80sr_main m; // main regs
|
||||
struct z80sr_main a; // alt (') regs
|
||||
u8 i, r;
|
||||
u16 ix, iy;
|
||||
u16 sp;
|
||||
u16 pc;
|
||||
// other
|
||||
u8 halted;
|
||||
u8 iff1, iff2;
|
||||
u8 im; // irq mode
|
||||
u8 irq_pending; // irq line level, 1 if active
|
||||
u8 irq_vector[3]; // up to 3 byte vector for irq mode0 handling
|
||||
u8 reserved[8];
|
||||
};
|
||||
|
||||
void z80_pack(void *data)
|
||||
{
|
||||
struct z80_state *s = data;
|
||||
memset(data, 0, Z80_STATE_SIZE);
|
||||
strcpy(s->magic, "Z80");
|
||||
#if defined(_USE_DRZ80)
|
||||
#define DRR8(n) (drZ80.Z80##n >> 24)
|
||||
#define DRR16(n) (drZ80.Z80##n >> 16)
|
||||
#define DRR16H(n) (drZ80.Z80##n >> 24)
|
||||
#define DRR16L(n) ((drZ80.Z80##n >> 16) & 0xff)
|
||||
s->m.a = DRR8(A); s->m.f = drZ80.Z80F;
|
||||
s->m.b = DRR16H(BC); s->m.c = DRR16L(BC);
|
||||
s->m.d = DRR16H(DE); s->m.e = DRR16L(DE);
|
||||
s->m.h = DRR16H(HL); s->m.l = DRR16L(HL);
|
||||
s->a.a = DRR8(A2); s->a.f = drZ80.Z80F2;
|
||||
s->a.b = DRR16H(BC2); s->a.c = DRR16L(BC2);
|
||||
s->a.d = DRR16H(DE2); s->a.e = DRR16L(DE2);
|
||||
s->a.h = DRR16H(HL2); s->a.l = DRR16L(HL2);
|
||||
s->i = DRR8(I); s->r = drZ80.spare;
|
||||
s->ix = DRR16(IX); s->iy = DRR16(IY);
|
||||
s->sp = drZ80.Z80SP - drZ80.Z80SP_BASE;
|
||||
s->pc = drZ80.Z80PC - drZ80.Z80PC_BASE;
|
||||
s->halted = !!(drZ80.Z80IF & 4);
|
||||
s->iff1 = !!(drZ80.Z80IF & 1);
|
||||
s->iff2 = !!(drZ80.Z80IF & 2);
|
||||
s->im = drZ80.Z80IM;
|
||||
s->irq_pending = !!drZ80.Z80_IRQ;
|
||||
s->irq_vector[0] = drZ80.z80irqvector >> 16;
|
||||
s->irq_vector[1] = drZ80.z80irqvector >> 8;
|
||||
s->irq_vector[2] = drZ80.z80irqvector;
|
||||
#elif defined(_USE_CZ80)
|
||||
{
|
||||
const cz80_struc *CPU = &CZ80;
|
||||
s->m.a = zA; s->m.f = zF;
|
||||
s->m.b = zB; s->m.c = zC;
|
||||
s->m.d = zD; s->m.e = zE;
|
||||
s->m.h = zH; s->m.l = zL;
|
||||
s->a.a = zA2; s->a.f = zF2;
|
||||
s->a.b = CZ80.BC2.B.H; s->a.c = CZ80.BC2.B.L;
|
||||
s->a.d = CZ80.DE2.B.H; s->a.e = CZ80.DE2.B.L;
|
||||
s->a.h = CZ80.HL2.B.H; s->a.l = CZ80.HL2.B.L;
|
||||
s->i = zI; s->r = zR;
|
||||
s->ix = zIX; s->iy = zIY;
|
||||
s->sp = Cz80_Get_Reg(&CZ80, CZ80_SP);
|
||||
s->pc = Cz80_Get_Reg(&CZ80, CZ80_PC);
|
||||
s->halted = !!Cz80_Get_Reg(&CZ80, CZ80_HALT);
|
||||
s->iff1 = !!zIFF1;
|
||||
s->iff2 = !!zIFF2;
|
||||
s->im = zIM;
|
||||
s->irq_pending = (Cz80_Get_Reg(&CZ80, CZ80_IRQ) == HOLD_LINE);
|
||||
s->irq_vector[0] = 0xff;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int z80_unpack(const void *data)
|
||||
{
|
||||
const struct z80_state *s = data;
|
||||
if (strcmp(s->magic, "Z80") != 0) {
|
||||
if (z80_unpack_legacy(data) != 0)
|
||||
goto fail;
|
||||
elprintf(EL_STATUS, "legacy z80 state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_USE_DRZ80)
|
||||
#define DRW8(n, v) drZ80.Z80##n = (u32)(v) << 24
|
||||
#define DRW16(n, v) drZ80.Z80##n = (u32)(v) << 16
|
||||
#define DRW16HL(n, h, l) drZ80.Z80##n = ((u32)(h) << 24) | ((u32)(l) << 16)
|
||||
DRW8(A, s->m.a); drZ80.Z80F = s->m.f;
|
||||
DRW16HL(BC, s->m.b, s->m.c);
|
||||
DRW16HL(DE, s->m.d, s->m.e);
|
||||
DRW16HL(HL, s->m.h, s->m.l);
|
||||
DRW8(A2, s->a.a); drZ80.Z80F2 = s->a.f;
|
||||
DRW16HL(BC2, s->a.b, s->a.c);
|
||||
DRW16HL(DE2, s->a.d, s->a.e);
|
||||
DRW16HL(HL2, s->a.h, s->a.l);
|
||||
DRW8(I, s->i); drZ80.spare = s->r;
|
||||
DRW16(IX, s->ix); DRW16(IY, s->iy);
|
||||
drz80_load_pcsp(s->pc, s->sp);
|
||||
drZ80.Z80IF = 0;
|
||||
if (s->halted) drZ80.Z80IF |= 4;
|
||||
if (s->iff1) drZ80.Z80IF |= 1;
|
||||
if (s->iff2) drZ80.Z80IF |= 2;
|
||||
drZ80.Z80IM = s->im;
|
||||
drZ80.Z80_IRQ = s->irq_pending;
|
||||
drZ80.z80irqvector = ((u32)s->irq_vector[0] << 16) |
|
||||
((u32)s->irq_vector[1] << 8) | s->irq_vector[2];
|
||||
return 0;
|
||||
#elif defined(_USE_CZ80)
|
||||
{
|
||||
cz80_struc *CPU = &CZ80;
|
||||
zA = s->m.a; zF = s->m.f;
|
||||
zB = s->m.b; zC = s->m.c;
|
||||
zD = s->m.d; zE = s->m.e;
|
||||
zH = s->m.h; zL = s->m.l;
|
||||
zA2 = s->a.a; zF2 = s->a.f;
|
||||
CZ80.BC2.B.H = s->a.b; CZ80.BC2.B.L = s->a.c;
|
||||
CZ80.DE2.B.H = s->a.d; CZ80.DE2.B.L = s->a.e;
|
||||
CZ80.HL2.B.H = s->a.h; CZ80.HL2.B.L = s->a.l;
|
||||
zI = s->i; zR = s->r;
|
||||
zIX = s->ix; zIY = s->iy;
|
||||
Cz80_Set_Reg(&CZ80, CZ80_SP, s->sp);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_PC, s->pc);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_HALT, s->halted);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_IFF1, s->iff1);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_IFF2, s->iff2);
|
||||
zIM = s->im;
|
||||
Cz80_Set_Reg(&CZ80, CZ80_IRQ, s->irq_pending ? HOLD_LINE : CLEAR_LINE);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
fail:
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "z80_unpack failed");
|
||||
z80_reset();
|
||||
z80_int();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void z80_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void z80_debug(char *dstr)
|
||||
{
|
||||
#if defined(_USE_DRZ80)
|
||||
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
|
||||
#elif defined(_USE_CZ80)
|
||||
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", (unsigned int)(CZ80.PC - CZ80.BasePC), CZ80.SP.W);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
#
|
||||
PicoDrive 1.xx
|
||||
|
||||
About
|
||||
-----
|
||||
#include "../README"
|
||||
|
||||
How to make it run
|
||||
------------------
|
||||
|
||||
#ifdef GP2X
|
||||
Extract all files to some directory on your SD and run PicoDrive.gpe from your
|
||||
GP2X/Wiz/Caanoo menu. The same .gpe supports GP2X F100/F200, Wiz and Caanoo,
|
||||
there is no need to use separate versions.
|
||||
Then load a ROM and enjoy! ROMs can be in .smd or .bin format and can be zipped.
|
||||
Sega/Mega CD images can be in ISO/CSO+MP3/WAV or CUE+BIN formats (read below
|
||||
for more details).
|
||||
#endif
|
||||
#ifdef GIZ
|
||||
First make sure you have homebrew-enabled Service Pack installed. Then copy
|
||||
PicoDrive.exe and KGSDK.dll to any place in your filesystem (both files must
|
||||
be in the same directory) and run PicoDrive.exe using the launcher of your choice
|
||||
(some of them might require renaming PicoDrive.exe to Autorun.exe, placing it in
|
||||
the root of SD, etc). Then load a ROM and enjoy! ROMs can be placed anywhere, can
|
||||
be in .smd or .bin format and can be zipped (one ROM per zip).
|
||||
#endif
|
||||
#ifdef PSP
|
||||
If you are running a custom firmware, just copy the whole PicoDrive directory to
|
||||
/PSP/GAME or /PSP/GAMEXXX directory in your memory stick (it shouldn't matter
|
||||
which one GAME* directory to use).
|
||||
|
||||
If you are on 1.5, there is a separate KXploited version for it.
|
||||
#endif
|
||||
#ifdef PANDORA
|
||||
Just copy the .pnd to <sd card>/pandora/menu or <sd card>/pandora/desktop.
|
||||
#endif
|
||||
|
||||
This emulator has lots of options with various tweaks (for improved speed mostly),
|
||||
but it should have best compatibility in it's default config. If suddenly you
|
||||
start getting glitches or change something and forget what, use "Restore defaults"
|
||||
option.
|
||||
|
||||
|
||||
How to run Sega/Mega CD games
|
||||
-----------------------------
|
||||
|
||||
To play any CD game, you need BIOS files. These files must be copied to
|
||||
#ifdef PANDORA
|
||||
<sd card>/pandora/appdata/picodrive/ directory
|
||||
(if you run PicoDrive once it will create that directory for you).
|
||||
#else
|
||||
the same directory as PicoDrive files.
|
||||
#endif
|
||||
Files can be named as follows:
|
||||
|
||||
US: us_scd1_9210.bin us_scd2_9306.bin SegaCDBIOS9303.bin
|
||||
EU: eu_mcd1_9210.bin eu_mcd2_9303.bin eu_mcd2_9306.bin
|
||||
JP: jp_mcd1_9112.bin jp_mcd1_9111.bin
|
||||
these files can also be zipped.
|
||||
|
||||
The game must be dumped to CUE+BIN or CUE+ISO format.
|
||||
ISO/CSO+MP3/WAV is also supported, but may cause problems.
|
||||
When using CUE/BIN, you must load .cue file from the menu, or else
|
||||
the emu will not find audio tracks.
|
||||
|
||||
|
||||
Other important stuff
|
||||
---------------------
|
||||
|
||||
* Sega/Mega CD: If the background music is missing, the CD image format may be
|
||||
wrong. Currently .cue/bin is recommended. Be aware that there are lots of bad
|
||||
dumps on the web, and some use mp3 format for audio, which often causes
|
||||
problems (see below).
|
||||
* While iso/mp3 format is supported, it's not recommended to use.
|
||||
Some of many problems with mp3 are listed below:
|
||||
* MP3s may be named incorrectly and will not play.
|
||||
* The game music may play too fast/too slow/out of sync, which means they
|
||||
are encoded incorrectly. PicoDrive is not a mp3 player, so all mp3s MUST
|
||||
be encoded at 44.1kHz stereo.
|
||||
* Sega/Mega CD: If your games hang at the BIOS screen (with planets shown),
|
||||
you may be using a bad BIOS dump. Try another from a different source,
|
||||
like dumping it from your own console.
|
||||
#ifdef GP2X
|
||||
* What using mp3s, use lower bitrate for better performance (96 or 128kbps
|
||||
CBRs recommended).
|
||||
* GP2X F100/F200: When you use both GP2X CPUs, keep in mind that you can't
|
||||
overclock as high as when using ARM920 only. For example my GP2X when run
|
||||
singlecore can reach 280MHz, but with both cores it's about 250MHz. When
|
||||
overclocked too much, it may start hanging and producing random noise, or
|
||||
causing ARM940 crashes ("940 crashed" message displayed).
|
||||
* GP2X F100/F200: Due to internal implementation mp3s must not be larger that
|
||||
12MB (12582912 bytes). Larger mp3s will not be fully loaded.
|
||||
#endif
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
@@0. "Save slot"
|
||||
This is a slot number to use for savestates, when done by a button press outside
|
||||
menu. This can also be configured to be changed with a button
|
||||
(see "key configuration").
|
||||
|
||||
@@0. "Frameskip"
|
||||
How many frames to skip rendering before displaying another.
|
||||
"Auto" is recommended.
|
||||
|
||||
@@0. "Region"
|
||||
This option lets you force the game to think it is running on machine from the
|
||||
specified region, or just to set autodetection order. Also affects Sega/Mega CD.
|
||||
|
||||
@@0. "Show FPS"
|
||||
Self-explanatory. Format is XX/YY, where XX is the number of rendered frames and
|
||||
YY is the number of emulated frames per second.
|
||||
|
||||
@@0. "Enable sound"
|
||||
Does what it says. You must enable at least YM2612 or SN76496 (in advanced options,
|
||||
see below) for this to make sense (already done by default).
|
||||
|
||||
@@0. "Sound Quality"
|
||||
#ifdef PSP
|
||||
Sound sample rate, affects sound quality and emulation performance.
|
||||
22050Hz setting is the recommended one.
|
||||
#else
|
||||
Sound sample rate and stereo mode. Mono is not available in Sega/Mega CD mode.
|
||||
#endif
|
||||
|
||||
@@0. "Confirm savestate"
|
||||
Allows to enable confirmation on savestate saving (to prevent savestate overwrites),
|
||||
on loading (to prevent destroying current game progress), and on both or none, when
|
||||
using shortcut buttons (not menu) for saving/loading.
|
||||
|
||||
@@0. "[Display options]"
|
||||
Enters Display options menu (see below).
|
||||
|
||||
@@0. "[Sega/Mega CD options]"
|
||||
Enters Sega/Mega CD options menu (see below).
|
||||
|
||||
@@0. "[32X options]"
|
||||
Enters 32X options menu (see below).
|
||||
|
||||
@@0. "[Advanced options]"
|
||||
Enters advanced options menu (see below).
|
||||
|
||||
@@0. "Save cfg as default"
|
||||
If you save your config here it will be loaded on next ROM load, but only if there
|
||||
is no game specific config saved (which will be loaded in that case).
|
||||
You can press left/right to switch to a different config profile.
|
||||
|
||||
@@0. "Save cfg for current game only"
|
||||
Whenever you load current ROM again these settings will be loaded.
|
||||
|
||||
@@0. "Restore defaults"
|
||||
Restores all options (except controls) to defaults.
|
||||
|
||||
|
||||
Display options
|
||||
---------------
|
||||
|
||||
#ifndef PANDORA
|
||||
@@1. "Renderer"
|
||||
#ifdef GP2X
|
||||
8bit fast:
|
||||
This enables alternative heavily optimized tile-based renderer, which renders
|
||||
pixels not line-by-line (this is what accurate renderers do), but in 8x8 tiles,
|
||||
which is much faster. But because of the way it works it can't render any
|
||||
mid-frame image changes (raster effects), so it is useful only with some games.
|
||||
|
||||
Other two are accurate line-based renderers. The 8bit is faster but does not
|
||||
run well with some games like Street Racer.
|
||||
|
||||
#endif
|
||||
#ifdef GIZ
|
||||
This option allows to switch between 16bit and 8bit renderers. The 8bit one is
|
||||
a bit faster for some games, but not much, because colors still need to be
|
||||
converted to 16bit, as this is what Gizmondo requires. It also introduces
|
||||
graphics problems for some games, so it's best to use 16bit one.
|
||||
|
||||
#endif
|
||||
#ifdef PSP
|
||||
This option allows to switch between fast and accurate renderers. The fast one
|
||||
is much faster, because it draws the whole frame at a time, instead of doing it
|
||||
line by line, like the accurate one does. But because of the way it works it
|
||||
can't render any mid-frame image changes (raster effects), so it is useful only
|
||||
for some games.
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#ifdef GP2X
|
||||
@@1. "Tearing Fix"
|
||||
Wiz only: works around the tearing problem by using portrait mode. Causes ~5-10%
|
||||
performance hit, but eliminates the tearing effect.
|
||||
|
||||
@@1. "Gamma correction"
|
||||
F100/F200 only: Alters image gamma through GP2X hardware. Larger values make
|
||||
image to look brighter, lower - darker (default is 1.0).
|
||||
|
||||
@@1. "Vsync"
|
||||
This one adjusts the LCD refresh rate to better match game's refresh rate and
|
||||
starts synchronizing rendering with it. Should make scrolling smoother and
|
||||
eliminate tearing on F100/F200.
|
||||
#endif
|
||||
#ifdef GIZ
|
||||
@@1. "Scanline mode"
|
||||
This option was designed to work around slow framebuffer access (the Gizmondo's
|
||||
main bottleneck) by drawing every other line (even numbered lines only).
|
||||
This improves performance greatly, but looses detail.
|
||||
|
||||
@@1. "Scale low res mode"
|
||||
The Genesis/Megadrive had several graphics modes, some of which were only 256
|
||||
pixels wide. This option scales their width to 320 by using simple
|
||||
pixel averaging scaling. Works only when 16bit renderer is enabled.
|
||||
|
||||
@@1. "Double buffering"
|
||||
Draws the display to offscreen buffer, and flips it with visible one when done.
|
||||
Unfortunately this causes serious tearing, unless v-sync is used (next option).
|
||||
|
||||
@@1. "Wait for V-sync"
|
||||
Waits for vertical sync before drawing (or flipping buffers, if previous option
|
||||
is enabled). Emulation is stopped while waiting, so this causes large performance
|
||||
hit.
|
||||
#endif
|
||||
#ifdef PSP
|
||||
@@1. "Scale factor"
|
||||
This allows to resize the displayed image by using the PSP's hardware. The number is
|
||||
used to multiply width and height of the game image to get the size of image to be
|
||||
displayed. If you just want to make it fullscreen, just use "Set to fullscreen"
|
||||
setting below.
|
||||
|
||||
@@1. "Hor. scale (for low res. games)"
|
||||
This one works similarly as the previous setting, but can be used to apply additional
|
||||
scaling horizontally, and is used for games which use lower (256 pixel wide) Gen/MD
|
||||
resolution.
|
||||
|
||||
@@1. "Hor. scale (for hi res. games)"
|
||||
Same as above, only for higher (320 pixel wide) resolution using games.
|
||||
|
||||
@@1. "Bilinear filtering"
|
||||
If this is enabled, PSP hardware will apply bilinear filtering on the resulting image,
|
||||
making it smoother, but blurry.
|
||||
|
||||
@@1. "Gamma adjustment"
|
||||
Color gamma can be adjusted with this.
|
||||
|
||||
@@1. "Black level"
|
||||
This can be used to reduce unwanted "ghosting" effect for dark games, by making
|
||||
black pixels brighter. Use in conjunction with "gamma adjustment" for more effect.
|
||||
|
||||
@@1. "Wait for v-sync"
|
||||
Wait for the screen to finish updating before switching to next frame, to avoid tearing.
|
||||
There are 3 options:
|
||||
* never: don't wait for vsync.
|
||||
* sometimes: wait only if emulator is running fast enough.
|
||||
* always: always wait (causes emulation slowdown).
|
||||
|
||||
@@1. "Set to unscaled centered"
|
||||
Adjust the resizing options to set game image to it's original size.
|
||||
|
||||
@@1. "Set to 4:3 scaled"
|
||||
Scale the image up, but keep 4:3 aspect, by adding black borders.
|
||||
|
||||
@@1. "Set to fullscreen"
|
||||
Adjust the resizing options to make the game image fullscreen.
|
||||
#endif
|
||||
#ifdef PANDORA
|
||||
Allows to set up scaling, filtering and vertical sync.
|
||||
#endif
|
||||
|
||||
|
||||
Sega/Mega CD options
|
||||
--------------------
|
||||
|
||||
@@2. "CD LEDs"
|
||||
The Sega/Mega CD unit had two blinking LEDs (red and green) on it. This option
|
||||
will display them on top-left corner of the screen.
|
||||
|
||||
@@2. "CDDA audio"
|
||||
This option enables CD audio playback.
|
||||
|
||||
@@2. "PCM audio"
|
||||
This enables 8 channel PCM sound source. It is required for some games to run,
|
||||
because they monitor state of this audio chip.
|
||||
|
||||
@@2. "Save RAM cart"
|
||||
Here you can enable 64K RAM cart. Format it in BIOS if you do.
|
||||
|
||||
@@2. "Scale/Rot. fx"
|
||||
The Sega/Mega CD had scaling/rotation chip, which allows effects similar to
|
||||
"Mode 7" effects in SNES. On slow systems like GP2X, disabling may improve
|
||||
performance but cause graphical glitches.
|
||||
|
||||
|
||||
32X options
|
||||
-----------
|
||||
|
||||
@@3. "32X enabled"
|
||||
Enables emulation of addon. Option only takes effect when ROM is reloaded.
|
||||
|
||||
#ifdef GP2X
|
||||
@@3. "32X renderer"
|
||||
This currently only affects how the Genesis/MD layers are rendered, which is
|
||||
same as "Renderer" in display options.
|
||||
|
||||
#endif
|
||||
@@3. "PWM sound"
|
||||
Emulates PWM sound portion of 32X hardware. Disabling this may greatly improve
|
||||
performance for games that dedicate one of SD2s for sound, but will cause
|
||||
missing sound effects and instruments.
|
||||
|
||||
@@3. "Master SH2 cycles" / "Slave SH2 cycles"
|
||||
This allows underclocking the 32X CPUs for better emulation performance. The
|
||||
number has the same meaning as cycles in DOSBox, which is cycles per millisecond.
|
||||
Underclocking too much may cause various in-game glitches.
|
||||
|
||||
|
||||
Advanced configuration
|
||||
----------------------
|
||||
|
||||
@@4. "Use SRAM/BRAM savestates"
|
||||
This will automatically read/write SRAM (or BRAM for Sega/Mega CD) savestates for
|
||||
games which are using them. SRAM is saved whenever you enter the menu or exit the
|
||||
emulator.
|
||||
|
||||
@@4. "Disable sprite limit"
|
||||
The MegaDrive/Genesis had a limit on how many sprites (usually smaller moving
|
||||
objects) can be displayed on single line. This option allows to disable that
|
||||
limit. Note that some games used this to hide unwanted things, so it is not
|
||||
always good to enable this option.
|
||||
|
||||
@@4. "Emulate Z80"
|
||||
Enables emulation of Z80 chip, which was mostly used to drive the other sound chips.
|
||||
Some games do complex sync with it, so you must enable it even if you don't use
|
||||
sound to be able to play them.
|
||||
|
||||
@@4. "Emulate YM2612 (FM)"
|
||||
This enables emulation of six-channel FM sound synthesizer chip, which was used to
|
||||
produce sound effects and music.
|
||||
|
||||
@@4. "Emulate SN76496 (PSG)"
|
||||
This enables emulation of PSG (programmable sound generation) sound chip for
|
||||
additional effects.
|
||||
|
||||
Note: if you change sound settings AFTER loading a ROM, you may need to reset
|
||||
game to get sound. This is because most games initialize sound chips on
|
||||
startup, and this data is lost when sound chips are being enabled/disabled.
|
||||
|
||||
@@4. "gzip savestates"
|
||||
This will always apply gzip compression on your savestates, allowing you to
|
||||
save some space and load/save time.
|
||||
|
||||
@@4. "Don't save last used ROM"
|
||||
This will disable writing last used ROM to config on exit (what might cause SD
|
||||
card corruption according to DaveC).
|
||||
|
||||
@@4. "Disable idle loop patching"
|
||||
Idle loop patching is used to improve performance, but may cause compatibility
|
||||
problems in some rare cases. Try disabling this if your game has problems.
|
||||
|
||||
@@4. "Disable frame limiter"
|
||||
This allows games to run faster then 50/60fps, useful for benchmarking.
|
||||
|
||||
#ifdef GP2X
|
||||
@@4. "Use ARM940 core for sound"
|
||||
F100/F200: This option causes PicoDrive to use ARM940T core (GP2X's second CPU)
|
||||
for sound (i.e. to generate YM2612 samples) to improve performance noticeably.
|
||||
It also decodes MP3s in Sega/Mega CD mode.
|
||||
|
||||
#endif
|
||||
@@4. "Enable dynarecs"
|
||||
This enables dynamic recompilation for SH2 and SVP CPU code,
|
||||
what improves emulation performance greatly.
|
||||
|
||||
|
||||
Key configuration
|
||||
-----------------
|
||||
|
||||
Select "Configure controls" from the main menu. Then select "Player 1" and you will
|
||||
see two columns. The left column lists names of Genesis/MD controller buttons, and
|
||||
the right column your handheld ones, which are assigned.
|
||||
|
||||
There is also option to enable 6 button pad (will allow you to configure XYZ
|
||||
buttons), and an option to set turbo rate (in Hz) for turbo buttons.
|
||||
|
||||
|
||||
Cheat support
|
||||
-------------
|
||||
|
||||
To use GG/patch codes, you must type them into your favorite text editor, one
|
||||
per line. Comments may follow code after a whitespace. Only GameGenie and
|
||||
Genecyst patch formats are supported.
|
||||
Examples:
|
||||
|
||||
Genecyst patch (this example is for Sonic):
|
||||
|
||||
00334A:0005 Start with five lives
|
||||
012D24:0001 Keep invincibility until end of stage
|
||||
009C76:5478 each ring worth 2
|
||||
009C76:5678 each ring worth 3
|
||||
...
|
||||
|
||||
Game Genie patch (for Sonic 2):
|
||||
|
||||
ACLA-ATD4 Hidden palace instead of death egg in level select
|
||||
...
|
||||
|
||||
Both GG and patch codes can be mixed in one file.
|
||||
|
||||
When the file is ready, name it just like your ROM file, but with additional
|
||||
.pat extension, making sure that case matches.
|
||||
|
||||
Examples:
|
||||
|
||||
ROM: Sonic.zip
|
||||
PATCH FILE: Sonic.zip.pat
|
||||
|
||||
ROM: Sonic 2.bin
|
||||
PATCH FILE: Sonic 2.bin.pat
|
||||
|
||||
Put the file into your ROMs directory. Then load the .pat file as you would
|
||||
a ROM. Then Cheat Menu Option should appear in main menu.
|
||||
|
||||
|
||||
What is emulated?
|
||||
-----------------
|
||||
|
||||
Genesis/MegaDrive:
|
||||
#ifdef PSP
|
||||
main 68k @ 7.6MHz: yes, FAME/C core
|
||||
z80 @ 3.6MHz: yes, CZ80 core
|
||||
#else
|
||||
main 68k @ 7.6MHz: yes, Cyclone core
|
||||
z80 @ 3.6MHz: yes, DrZ80 core
|
||||
#endif
|
||||
VDP: yes, except some quirks and modes not used by games
|
||||
YM2612 FM: yes, optimized MAME core
|
||||
SN76489 PSG: yes, MAME core
|
||||
SVP chip: yes! This is first emu to ever do this.
|
||||
Some in-cart mappers are also supported.
|
||||
|
||||
Sega/Mega CD:
|
||||
#ifdef PSP
|
||||
another 68k @ 12.5MHz: yes, FAME/C too
|
||||
#else
|
||||
another 68k @ 12.5MHz: yes, Cyclone too
|
||||
#endif
|
||||
gfx scaling/rotation chip (custom ASIC): yes
|
||||
PCM sound source: yes
|
||||
CD-ROM controller: yes (mostly)
|
||||
bram (internal backup RAM): yes
|
||||
|
||||
32X:
|
||||
2x SH2 @ 23MHz: yes, custom recompiler
|
||||
Super VDP: yes
|
||||
PWM: yes
|
||||
|
||||
|
||||
Problems / limitations
|
||||
----------------------
|
||||
|
||||
#ifdef PSP
|
||||
* SVP emulation is terribly slow.
|
||||
#endif
|
||||
* Various VDP modes and quirks (window bug, scroll size 2, etc.) are not
|
||||
emulated, as very few games use this (if any at all).
|
||||
* The emulator is not 100% accurate, so some things may not work as expected.
|
||||
* The FM sound core doesn't support all features and has some accuracy issues.
|
||||
|
||||
|
||||
Changelog
|
||||
-------
|
||||
|
||||
#include "../ChangeLog"
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
This emulator is made of the code from following people/projects:
|
||||
|
||||
#include "../AUTHORS"
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This program and it's code is released under the terms of MAME license:
|
||||
#include "../COPYING"
|
||||
|
||||
SEGA/Genesis/MegaDrive/SEGA-CD/Mega-CD/32X are trademarks of
|
||||
Sega Enterprises Ltd.
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
#
|
||||
# Sega/Mega CD games
|
||||
#
|
||||
|
||||
[CD|GM MK-4432 -00|U|ADVENTURES OF BA]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM T-60055-00|U|AH3-THUNDERSTRIKE]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM MK-4401 -00|U|BATMAN RETURNS]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM T-115075-00|U|BCRACERS]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM MK-4402|U|COBRA COMMAND]
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM T-121015-00|U|DRAGONS LAIR]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-60094|J|JAGUAR XJ220]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-127015-00|U|LUNAR]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM T-111065 -0|U|MAD DOG II THE LOST GOLD]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-11105 -00|U|MAD DOG MCCREE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-81025-00|U|MORTAL KOMBAT]
|
||||
Renderer = 16bit accurate
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM T-04903-01|U|NIGHT TRAP]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-113025-00|U|NOVASTORM]
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM T-127035-00|U|Popful MAIL]
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|MK 4603-50|JUE|ROAD AVENGER]
|
||||
Renderer = 16bit accurate
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM T-50085|U|ROAD RASH]
|
||||
Renderer = 16bit accurate
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM MK-4416 -00|E|ROBO ALESTE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-06201-03|U|SEWER SHARK]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM T-113045-00|E|SHADOW OF THE BEAST TWO]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[CD|GM MK-4404|U|SOL-FEACE]
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM MK-4407-00|E|SONIC THE HEDGEHOG-CD]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM MK-4407 -00|U|SONIC THE HEDGEHOG-CD]
|
||||
Scale/Rot. fx (slow) = 1
|
||||
|
||||
[CD|GM T-22025-00|U|THE 3RD WORLD WAR]
|
||||
Better sync (slow) = 1
|
||||
|
||||
[CD|GM MK- 4430 -|E|YUMEMI MISTERY MANSION]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
#
|
||||
# Virtua Racing
|
||||
#
|
||||
[MD|GM MK-1229 -00|U|Virtua Racing]
|
||||
Renderer = 8bit fast
|
||||
Show FPS = 1
|
||||
GP2X CPU clocks = 235
|
||||
|
||||
[MD|GM G-7001 -00|J|Virtua Racing]
|
||||
Renderer = 8bit fast
|
||||
Show FPS = 1
|
||||
GP2X CPU clocks = 235
|
||||
|
||||
[MD|GM MK-1229 -00|E|Virtua Racing]
|
||||
Renderer = 8bit fast
|
||||
Show FPS = 1
|
||||
GP2X CPU clocks = 235
|
||||
|
||||
[MD|GM MK-1229 -00|E|VIRTUA RACING \00\00\00\00\00\00\00\00\00]
|
||||
Renderer = 8bit fast
|
||||
Show FPS = 1
|
||||
GP2X CPU clocks = 235
|
||||
|
||||
#
|
||||
# Genesis/MegaDrive games
|
||||
#
|
||||
[MD|GM MK-1029-00|E| BURNING FORCE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-14023 -00|J| BURNING FORCE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-14026 -00|U| BURNING FORCE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-95076-00|U|CASTLEVANIA BLOODLINES]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-95076-00|E|CASTLEVANIA THE NEW GENERATION]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM MK-1569 -50|A|COMIX ZONE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM G-4132 -00|1|]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM MK-1569 -00|4|COMIX ZONE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-097116 00|4|MORTAL KOMBAT 3]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM G-4119-00|J| OUT_RUNNERS 1994/03/03 Ver.FINAL!!]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-13096-00|U| OUT_RUNNERS]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-48376 00|JUE|RED-ZONE]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-177016-00|F|STREET RACER]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-95043-00|J|]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-25036 -00|U| SHOVE IT!]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM MK-1104 -00|U|AFTER BURNERII]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-32053 -00|U|ARCUS ODYSSEY]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM 00054010-00|JUE|RAMBO \87V]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM 00054010-01|JUE|RAMBO \87V]
|
||||
Renderer = 16bit accurate
|
||||
|
||||
[MD|GM T-50406 -01|U|GALAHAD]
|
||||
Renderer = 16bit accurate
|
||||
|
Loading…
Reference in New Issue