diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b1339dc7bd..3066f74cdd 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -992,6 +992,8 @@ + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/LibVirtualBoyee.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/LibVirtualBoyee.cs new file mode 100644 index 0000000000..968403112f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/LibVirtualBoyee.cs @@ -0,0 +1,92 @@ +using BizHawk.Common.BizInvoke; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB +{ + public abstract class LibVirtualBoyee + { + private const CallingConvention CC = CallingConvention.Cdecl; + + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + public int X; + public int Y; + public int W; + public int H; + } + + [StructLayout(LayoutKind.Sequential)] + public class EmulateSpec + { + // Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system. + // Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system. + // The framebuffer pointed to by surface->pixels is written to by the system emulation code. + public IntPtr Pixels; + + // Pointer to sound buffer, set by the driver code, that the emulation code should render sound to. + // Guaranteed to be at least 500ms in length, but emulation code really shouldn't exceed 40ms or so. Additionally, if emulation code + // generates >= 100ms, + // DEPRECATED: Emulation code may set this pointer to a sound buffer internal to the emulation module. + public IntPtr SoundBuf; + + // Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base. + // Set by emulation code. + public long MasterCycles; + + // Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size + // of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure + // is ignored while drawing the image. + public Rect DisplayRect; + + // Maximum size of the sound buffer, in frames. Set by the driver code. + public int SoundBufMaxSize; + + // Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code. + public int SoundBufSize; + + // 0 UDLR SelectStartBA UDLR(right dpad) LtrigRtrig 13 + public Buttons Buttons; + } + + public enum MemoryArea : int + { + Wram, Sram, Rom + } + + public enum Buttons : int + { + Up = 0x1, + Down = 0x2, + Left = 0x4, + Right = 0x8, + Select = 0x10, + Start = 0x20, + B = 0x40, + A = 0x80, + Up_R = 0x100, + Down_R = 0x200, + Left_R = 0x400, + Right_R = 0x800, + L = 0x1000, + R = 0x2000 + } + + [BizImport(CC)] + public abstract bool Load(byte[] rom, int length); + + [BizImport(CC)] + public abstract void GetMemoryArea(MemoryArea which, ref IntPtr ptr, ref int size); + + [BizImport(CC)] + public abstract void Emulate(EmulateSpec espec); + + [BizImport(CC)] + public abstract void HardReset(); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs new file mode 100644 index 0000000000..05e84aebf7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs @@ -0,0 +1,135 @@ +using BizHawk.Common.BizInvoke; +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.Nintendo.VB +{ + public class VirtualBoyee : IEmulator, IVideoProvider + { + private PeRunner _exe; + private LibVirtualBoyee _boyee; + + [CoreConstructor("VB")] + public VirtualBoyee(CoreComm comm, byte[] rom) + { + ServiceProvider = new BasicServiceProvider(this); + CoreComm = comm; + + _exe = new PeRunner(new PeRunnerOptions + { + Path = comm.CoreFileProvider.DllPath(), + Filename = "vb.wbx", + NormalHeapSizeKB = 1024, + SealedHeapSizeKB = 12 * 1024, + InvisibleHeapSizeKB = 6 * 1024, + SpecialHeapSizeKB = 64 + }); + + _boyee = BizInvoker.GetInvoker(_exe, _exe); + + if (!_boyee.Load(rom, rom.Length)) + { + throw new InvalidOperationException("Core rejected the rom"); + } + } + + private bool _disposed = false; + + public void Dispose() + { + if (!_disposed) + { + _exe.Dispose(); + _exe = null; + _disposed = true; + } + } + + public IEmulatorServiceProvider ServiceProvider { get; private set; } + + public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true) + { + var scratch = new short[16384]; + + fixed(int*vp = _videoBuffer) + fixed(short*sp = scratch) + { + var spec = new LibVirtualBoyee.EmulateSpec + { + Pixels = (IntPtr)vp, + SoundBuf = (IntPtr)sp, + SoundBufMaxSize = 8192 + }; + + _boyee.Emulate(spec); + VirtualWidth = BufferWidth = spec.DisplayRect.W; + VirtualWidth = BufferHeight = spec.DisplayRect.H; + Console.WriteLine(spec.SoundBufSize); + } + + Frame++; + + /*_core.biz_set_input_callback(InputCallbacks.Count > 0 ? _inputCallback : null); + + if (controller.IsPressed("Power")) + _core.biz_hard_reset(); + else if (controller.IsPressed("Reset")) + _core.biz_soft_reset(); + + UpdateControls(controller); + Frame++; + LibSnes9x.frame_info frame = new LibSnes9x.frame_info(); + + _core.biz_run(frame, _inputState); + IsLagFrame = frame.padread == 0; + if (IsLagFrame) + LagCount++; + using (_exe.EnterExit()) + { + Blit(frame); + Sblit(frame); + }*/ + } + + public int Frame { get; private set; } + + public void ResetCounters() + { + Frame = 0; + } + + public string SystemId { get { return "VB"; } } + public bool DeterministicEmulation { get { return true; } } + public CoreComm CoreComm { get; private set; } + + public ControllerDefinition ControllerDefinition => NullController.Instance.Definition; + + #region IVideoProvider + + private int[] _videoBuffer = new int[0]; + + public int[] GetVideoBuffer() + { + throw new NotImplementedException(); + } + + public int VirtualWidth { get; private set; } + public int VirtualHeight { get; private set; } + + public int BufferWidth { get; private set; } + public int BufferHeight { get; private set; } + + public int VsyncNumerator { get; private set; } + + public int VsyncDenominator { get; private set; } + + public int BackgroundColor => unchecked((int)0xff000000); + + #endregion + } +} diff --git a/waterbox/vb/.vscode/settings.json b/waterbox/vb/.vscode/settings.json new file mode 100644 index 0000000000..edb0a83e80 --- /dev/null +++ b/waterbox/vb/.vscode/settings.json @@ -0,0 +1,9 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "editor.detectIndentation": false, + "editor.insertSpaces": false, + "files.associations": { + "xiosbase": "cpp", + "xlocale": "cpp" + } +} \ No newline at end of file diff --git a/waterbox/vb/Makefile b/waterbox/vb/Makefile new file mode 100644 index 0000000000..b3f5d5b4e4 --- /dev/null +++ b/waterbox/vb/Makefile @@ -0,0 +1,43 @@ +CC = x86_64-nt64-midipix-g++ + +CCFLAGS:= -I. -I../emulibc \ + -Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \ + -std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \ + -DLSB_FIRST \ + -O0 + +TARGET = vb.wbx + +LDFLAGS = -Wl,--dynamicbase,--export-all-symbols + +ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +SRCS:=$(shell find $(ROOT_DIR) -type f -name '*.cpp') +OBJ_DIR:=$(ROOT_DIR)/obj + +_OBJS:=$(SRCS:.cpp=.o) +OBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS)) + +$(OBJ_DIR)/%.o: %.cpp + @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 + +clean: + rm -rf $(OBJ_DIR) + rm -f $(TARGET).in + rm -f $(TARGET) + +print-%: + @echo $* = $($*) + +#install: +# $(CP) $(TARGET) $(DEST_$(ARCH)) diff --git a/waterbox/vb/blip/Blip_Buffer.cpp b/waterbox/vb/blip/Blip_Buffer.cpp new file mode 100644 index 0000000000..f04a1fc599 --- /dev/null +++ b/waterbox/vb/blip/Blip_Buffer.cpp @@ -0,0 +1,457 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = (blip_u64)ULLONG_MAX; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + blip_s64 new_size = (ULLONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + + // simple safety check, since code elsewhere may not be safe for sizes approaching (2 ^ 31). + if(new_size > ((1LL << 30) - 1)) + new_size = (1LL << 30) - 1; + + if ( msec != blip_max_length ) + { + blip_s64 s = ((blip_s64)new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + + //if(new_size > buffer_size_) + // memset(buffer_ + buffer_size_, 0, (new_size + blip_buffer_extra_) * sizeof *buffer_ + + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_s64 factor = (blip_s64) floor( ratio * (1LL << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; + //printf("%d\n", bass_shift_); +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size_local = this->impulses_size(); + for ( i = 0; i < impulses_size_local; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } + } + else + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } + } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/waterbox/vb/blip/Blip_Buffer.h b/waterbox/vb/blip/Blip_Buffer.h new file mode 100644 index 0000000000..a8e90ee053 --- /dev/null +++ b/waterbox/vb/blip/Blip_Buffer.h @@ -0,0 +1,498 @@ +// Band-limited sound synthesis buffer +// Various changes and hacks for use in Mednafen. + +#ifdef __GNUC__ + #define blip_inline inline __attribute__((always_inline)) +#else + #define blip_inline inline +#endif + +#include +#include + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// Internal +typedef int32_t blip_long; +typedef uint32_t blip_ulong; +typedef int64_t blip_s64; +typedef uint64_t blip_u64; + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_u64 blip_resampled_time_t; + void remove_silence( long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_u64 factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#define BLIP_BUFFER_ACCURACY 32 +#define BLIP_PHASE_BITS 8 + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +//#ifndef BLIP_BUFFER_ACCURACY +// #define BLIP_BUFFER_ACCURACY 16 +//#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +//#ifndef BLIP_PHASE_BITS +// #if BLIP_BUFFER_FAST +// #define BLIP_PHASE_BITS 8 +// #else +// #define BLIP_PHASE_BITS 6 +// #endif +//#endif + + // Internal + typedef blip_u64 blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +blip_inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +blip_inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +blip_inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +blip_inline int Blip_Buffer::length() const { return length_; } +blip_inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +blip_inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +blip_inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +blip_inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +blip_inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +blip_inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/waterbox/vb/endian.h b/waterbox/vb/endian.h new file mode 100644 index 0000000000..e78a0c577f --- /dev/null +++ b/waterbox/vb/endian.h @@ -0,0 +1,494 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* endian.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MDFN_ENDIAN_H +#define __MDFN_ENDIAN_H + +void Endian_A16_Swap(void *src, uint32 nelements); +void Endian_A32_Swap(void *src, uint32 nelements); +void Endian_A64_Swap(void *src, uint32 nelements); + +void Endian_A16_NE_LE(void *src, uint32 nelements); +void Endian_A32_NE_LE(void *src, uint32 nelements); +void Endian_A64_NE_LE(void *src, uint32 nelements); + +void Endian_A16_NE_BE(void *src, uint32 nelements); +void Endian_A32_NE_BE(void *src, uint32 nelements); +void Endian_A64_NE_BE(void *src, uint32 nelements); + +void Endian_V_NE_LE(void* p, size_t len); +void Endian_V_NE_BE(void* p, size_t len); + +// +// +// + +static INLINE uint32 BitsExtract(const uint8* ptr, const size_t bit_offset, const size_t bit_count) +{ + uint32 ret = 0; + + for(size_t x = 0; x < bit_count; x++) + { + size_t co = bit_offset + x; + bool b = (ptr[co >> 3] >> (co & 7)) & 1; + + ret |= (uint64)b << x; + } + + return ret; +} + +static INLINE void BitsIntract(uint8* ptr, const size_t bit_offset, const size_t bit_count, uint32 value) +{ + for(size_t x = 0; x < bit_count; x++) + { + size_t co = bit_offset + x; + bool b = (value >> x) & 1; + uint8 tmp = ptr[co >> 3]; + + tmp &= ~(1 << (co & 7)); + tmp |= b << (co & 7); + + ptr[co >> 3] = tmp; + } +} + +/* + Regarding safety of calling MDFN_*sb on dynamically-allocated memory with new uint8[], see C++ standard 3.7.3.1(i.e. it should be + safe provided the offsets into the memory are aligned/multiples of the MDFN_*sb access type). malloc()'d and calloc()'d + memory should be safe as well. + + Statically-allocated arrays/memory should be unioned with a big POD type or C++11 "alignas"'d. (May need to audit code to ensure + this is being done). +*/ + +static INLINE uint16 MDFN_bswap16(uint16 v) +{ + return (v << 8) | (v >> 8); +} + +static INLINE uint32 MDFN_bswap32(uint32 v) +{ + return (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24); +} + +static INLINE uint64 MDFN_bswap64(uint64 v) +{ + return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(v >> 16) << 16); +} + +#ifdef LSB_FIRST + #define MDFN_ENDIANH_IS_BIGENDIAN 0 +#else + #define MDFN_ENDIANH_IS_BIGENDIAN 1 +#endif + +// +// X endian. +// +template +static INLINE T MDFN_deXsb(const void* ptr) +{ + T tmp; + + memcpy(&tmp, MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), sizeof(T)); + + if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN) + { + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins."); + + if(sizeof(T) == 8) + return MDFN_bswap64(tmp); + else if(sizeof(T) == 4) + return MDFN_bswap32(tmp); + else if(sizeof(T) == 2) + return MDFN_bswap16(tmp); + } + + return tmp; +} + +// +// Native endian. +// +template +static INLINE T MDFN_densb(const void* ptr) +{ + return MDFN_deXsb<-1, T, aligned>(ptr); +} + +// +// Little endian. +// +template +static INLINE T MDFN_delsb(const void* ptr) +{ + return MDFN_deXsb<0, T, aligned>(ptr); +} + +template +static INLINE uint16 MDFN_de16lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +static INLINE uint32 MDFN_de24lsb(const void* ptr) +{ + const uint8* ptr_u8 = (const uint8*)ptr; + + return (ptr_u8[0] << 0) | (ptr_u8[1] << 8) | (ptr_u8[2] << 16); +} + +template +static INLINE uint32 MDFN_de32lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +template +static INLINE uint64 MDFN_de64lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +// +// Big endian. +// +template +static INLINE T MDFN_demsb(const void* ptr) +{ + return MDFN_deXsb<1, T, aligned>(ptr); +} + +template +static INLINE uint16 MDFN_de16msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +static INLINE uint32 MDFN_de24msb(const void* ptr) +{ + const uint8* ptr_u8 = (const uint8*)ptr; + + return (ptr_u8[0] << 16) | (ptr_u8[1] << 8) | (ptr_u8[2] << 0); +} + +template +static INLINE uint32 MDFN_de32msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +template +static INLINE uint64 MDFN_de64msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +// +// +// +// +// +// +// +// + +// +// X endian. +// +template +static INLINE void MDFN_enXsb(void* ptr, T value) +{ + T tmp = value; + + if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN) + { + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins."); + + if(sizeof(T) == 8) + tmp = MDFN_bswap64(value); + else if(sizeof(T) == 4) + tmp = MDFN_bswap32(value); + else if(sizeof(T) == 2) + tmp = MDFN_bswap16(value); + } + + memcpy(MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), &tmp, sizeof(T)); +} + +// +// Native endian. +// +template +static INLINE void MDFN_ennsb(void* ptr, T value) +{ + MDFN_enXsb<-1, T, aligned>(ptr, value); +} + +// +// Little endian. +// +template +static INLINE void MDFN_enlsb(void* ptr, T value) +{ + MDFN_enXsb<0, T, aligned>(ptr, value); +} + +template +static INLINE void MDFN_en16lsb(void* ptr, uint16 value) +{ + MDFN_enlsb(ptr, value); +} + +static INLINE void MDFN_en24lsb(void* ptr, uint32 value) +{ + uint8* ptr_u8 = (uint8*)ptr; + + ptr_u8[0] = value >> 0; + ptr_u8[1] = value >> 8; + ptr_u8[2] = value >> 16; +} + +template +static INLINE void MDFN_en32lsb(void* ptr, uint32 value) +{ + MDFN_enlsb(ptr, value); +} + +template +static INLINE void MDFN_en64lsb(void* ptr, uint64 value) +{ + MDFN_enlsb(ptr, value); +} + + +// +// Big endian. +// +template +static INLINE void MDFN_enmsb(void* ptr, T value) +{ + MDFN_enXsb<1, T, aligned>(ptr, value); +} + +template +static INLINE void MDFN_en16msb(void* ptr, uint16 value) +{ + MDFN_enmsb(ptr, value); +} + +static INLINE void MDFN_en24msb(void* ptr, uint32 value) +{ + uint8* ptr_u8 = (uint8*)ptr; + + ptr_u8[0] = value >> 16; + ptr_u8[1] = value >> 8; + ptr_u8[2] = value >> 0; +} + +template +static INLINE void MDFN_en32msb(void* ptr, uint32 value) +{ + MDFN_enmsb(ptr, value); +} + +template +static INLINE void MDFN_en64msb(void* ptr, uint64 value) +{ + MDFN_enmsb(ptr, value); +} + + +// +// +// +// +// +// + +template +static INLINE uint8* ne16_ptr_be(BT* const base, const size_t byte_offset) +{ +#ifdef MSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (2 - std::min(2, sizeof(T))))); +#endif +} + +template +static INLINE void ne16_wbo_be(uint16* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne16_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + + ptr16[0] = value >> 16; + ptr16[1] = value; + } + else + *(T*)ptr = value; +} + +template +static INLINE T ne16_rbo_be(const uint16* const base, const size_t byte_offset) +{ + uint8* const ptr = ne16_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + T tmp; + + tmp = ptr16[0] << 16; + tmp |= ptr16[1]; + + return tmp; + } + else + return *(T*)ptr; +} + +template +static INLINE void ne16_rwbo_be(uint16* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne16_wbo_be(base, byte_offset, *value); + else + *value = ne16_rbo_be(base, byte_offset); +} + +// +// +// + +template +static INLINE uint8* ne16_ptr_le(BT* const base, const size_t byte_offset) +{ +#ifdef LSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (2 - std::min(2, sizeof(T))))); +#endif +} + +template +static INLINE void ne16_wbo_le(uint16* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne16_ptr_le(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + + ptr16[0] = value; + ptr16[1] = value >> 16; + } + else + *(T*)ptr = value; +} + +template +static INLINE T ne16_rbo_le(const uint16* const base, const size_t byte_offset) +{ + uint8* const ptr = ne16_ptr_le(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + T tmp; + + tmp = ptr16[0]; + tmp |= ptr16[1] << 16; + + return tmp; + } + else + return *(T*)ptr; +} + + +template +static INLINE void ne16_rwbo_le(uint16* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne16_wbo_le(base, byte_offset, *value); + else + *value = ne16_rbo_le(base, byte_offset); +} + +// +// +// +template +static INLINE uint8* ne64_ptr_be(uint64* const base, const size_t byte_offset) +{ +#ifdef MSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (8 - sizeof(T)))); +#endif +} + +template +static INLINE void ne64_wbo_be(uint64* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne64_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size"); + + memcpy(MDFN_ASSUME_ALIGNED(ptr, sizeof(T)), &value, sizeof(T)); +} + +template +static INLINE T ne64_rbo_be(uint64* const base, const size_t byte_offset) +{ + uint8* const ptr = ne64_ptr_be(base, byte_offset); + T ret; + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + memcpy(&ret, MDFN_ASSUME_ALIGNED(ptr, sizeof(T)), sizeof(T)); + + return ret; +} + +template +static INLINE void ne64_rwbo_be(uint64* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne64_wbo_be(base, byte_offset, *value); + else + *value = ne64_rbo_be(base, byte_offset); +} + +#endif diff --git a/waterbox/vb/git.h b/waterbox/vb/git.h new file mode 100644 index 0000000000..edcd8a8415 --- /dev/null +++ b/waterbox/vb/git.h @@ -0,0 +1,127 @@ +#pragma once + +struct MDFN_Surface +{ + uint32 *pixels; + int pitch32; +}; + +struct MDFN_Rect +{ + int x, y, w, h; +}; + +struct EmulateSpecStruct +{ + // Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system. + // Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system. + // The framebuffer pointed to by surface->pixels is written to by the system emulation code. + uint32 *pixels; + + // Pointer to sound buffer, set by the driver code, that the emulation code should render sound to. + // Guaranteed to be at least 500ms in length, but emulation code really shouldn't exceed 40ms or so. Additionally, if emulation code + // generates >= 100ms, + // DEPRECATED: Emulation code may set this pointer to a sound buffer internal to the emulation module. + int16 *SoundBuf; + + // Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base. + // Set by emulation code. + int64 MasterCycles; + + // Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size + // of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure + // is ignored while drawing the image. + MDFN_Rect DisplayRect; + + // Maximum size of the sound buffer, in frames. Set by the driver code. + int32 SoundBufMaxSize; + + // Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code. + int32 SoundBufSize; + + // 0 UDLR SelectStartBA UDLR(right dpad) LtrigRtrig 13 + int32 Buttons; +}; + +/*typedef struct +{ + + void (*Emulate)(EmulateSpecStruct *espec); + void (*TransformInput)(void); // Called before Emulate, and within MDFN_MidSync(), to implement stuff like setting-controlled PC Engine SEL+RUN button exclusion in a way + // that won't cause desyncs with movies and netplay. + + void (*SetInput)(unsigned port, const char *type, uint8* data); + bool (*SetMedia)(uint32 drive_idx, uint32 state_idx, uint32 media_idx, uint32 orientation_idx); + + + // Called when netplay starts, or the controllers controlled by local players changes during + // an existing netplay session. Called with ~(uint64)0 when netplay ends. + // (For future use in implementing portable console netplay) + void (*NPControlNotif)(uint64 c); + + const MDFNSetting *Settings; + + // Time base for EmulateSpecStruct::MasterCycles + // MasterClock must be >= MDFN_MASTERCLOCK_FIXED(1.0) + // All or part of the fractional component may be ignored in some timekeeping operations in the emulator to prevent integer overflow, + // so it is unwise to have a fractional component when the integral component is very small(less than say, 10000). + #define MDFN_MASTERCLOCK_FIXED(n) ((int64)((double)(n) * (1LL << 32))) + int64 MasterClock; + + // Nominal frames per second * 65536 * 256, truncated. + // May be deprecated in the future due to many systems having slight frame rate programmability. + uint32 fps; + + // multires is a hint that, if set, indicates that the system has fairly programmable video modes(particularly, the ability + // to display multiple horizontal resolutions, such as the PCE, PC-FX, or Genesis). In practice, it will cause the driver + // code to set the linear interpolation on by default. + // + // lcm_width and lcm_height are the least common multiples of all possible + // resolutions in the frame buffer as specified by DisplayRect/LineWidths(Ex for PCE: widths of 256, 341.333333, 512, + // lcm = 1024) + // + // nominal_width and nominal_height specify the resolution that Mednafen should display + // the framebuffer image in at 1x scaling, scaled from the dimensions of DisplayRect, and optionally the LineWidths array + // passed through espec to the Emulate() function. + // + bool multires; + + int lcm_width; + int lcm_height; + + + int nominal_width; + int nominal_height; + + int fb_width; // Width of the framebuffer(not necessarily width of the image). MDFN_Surface width should be >= this. + int fb_height; // Height of the framebuffer passed to the Emulate() function(not necessarily height of the image) + + int soundchan; // Number of output sound channels. Only values of 1 and 2 are currently supported. + + + int rotated; + + std::string name; // Game name, UTF-8 encoding + uint8 MD5[16]; + uint8 GameSetMD5[16]; // A unique ID for the game set this CD belongs to, only used in PC-FX emulation. + bool GameSetMD5Valid; // True if GameSetMD5 is valid. + + VideoSystems VideoSystem; + GameMediumTypes GameType; // Deprecated. + + RMD_Layout* RMD; + + const char *cspecial; // Special cart expansion: DIP switches, barcode reader, etc. + + std::vectorDesiredInput; // Desired input device for the input ports, NULL for don't care + + // For mouse relative motion. + double mouse_sensitivity; + + + // + // For absolute coordinates(IDIT_X_AXIS and IDIT_Y_AXIS), usually mapped to a mouse(hence the naming). + // + float mouse_scale_x, mouse_scale_y; + float mouse_offs_x, mouse_offs_y; +} MDFNGI;*/ diff --git a/waterbox/vb/input.cpp b/waterbox/vb/input.cpp new file mode 100644 index 0000000000..b895f9a5d9 --- /dev/null +++ b/waterbox/vb/input.cpp @@ -0,0 +1,196 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* input.cpp: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" +#include "input.h" + +namespace MDFN_IEN_VB +{ +static bool InstantReadHack; + +static bool IntPending; + +static uint16 PadData; +static uint16 PadLatched; + +static uint8 SCR; +static uint16 SDR; + +#define SCR_S_ABT_DIS 0x01 +#define SCR_SI_STAT 0x02 +#define SCR_HW_SI 0x04 +#define SCR_SOFT_CLK 0x10 + +#define SCR_PARA_SI 0x20 +#define SCR_K_INT_INH 0x80 + +static uint32 ReadBitPos; +static int32 ReadCounter; + +static v810_timestamp_t last_ts; + +void VBINPUT_Init(void) +{ + InstantReadHack = true; +} + +void VBINPUT_SetInstantReadHack(bool enabled) +{ + InstantReadHack = enabled; +} + +uint8 VBINPUT_Read(v810_timestamp_t ×tamp, uint32 A) +{ + uint8 ret = 0; + + VBINPUT_Update(timestamp); + + //if(((A & 0xFF) == 0x10 || (A & 0xFF) == 0x14)) + // printf("Read %d\n", timestamp); + + //if(((A & 0xFF) == 0x10 || (A & 0xFF) == 0x14) && ReadCounter > 0) + //{ + // printf("Input port read during hardware transfer: %08x %d\n", A, timestamp); + //} + + switch (A & 0xFF) + { + case 0x10: + if (InstantReadHack) + ret = PadData; + else + ret = SDR & 0xFF; + break; + + case 0x14: + if (InstantReadHack) + ret = PadData >> 8; + else + ret = SDR >> 8; + break; + + case 0x28: + ret = SCR | (0x40 | 0x08 | SCR_HW_SI); + if (ReadCounter > 0) + ret |= SCR_SI_STAT; + break; + } + + // printf("Input Read: %08x %02x\n", A, ret); + VB_SetEvent(VB_EVENT_INPUT, (ReadCounter > 0) ? (timestamp + ReadCounter) : VB_EVENT_NONONO); + + return (ret); +} + +void VBINPUT_Write(v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + VBINPUT_Update(timestamp); + + //printf("Input write: %d, %08x %02x\n", timestamp, A, V); + switch (A & 0xFF) + { + case 0x28: + if ((V & SCR_HW_SI) && !(SCR & SCR_S_ABT_DIS) && ReadCounter <= 0) + { + //printf("Start Read: %d\n", timestamp); + PadLatched = PadData; + ReadBitPos = 0; + ReadCounter = 640; + } + + if (V & SCR_S_ABT_DIS) + { + ReadCounter = 0; + ReadBitPos = 0; + } + + if (V & SCR_K_INT_INH) + { + IntPending = false; + VBIRQ_Assert(VBIRQ_SOURCE_INPUT, IntPending); + } + + SCR = V & (0x80 | 0x20 | 0x10 | 1); + break; + } + + VB_SetEvent(VB_EVENT_INPUT, (ReadCounter > 0) ? (timestamp + ReadCounter) : VB_EVENT_NONONO); +} + +void VBINPUT_Frame(const void* ptr) +{ + PadData = (MDFN_de16lsb(ptr) << 2) | 0x2; +} + +v810_timestamp_t VBINPUT_Update(const v810_timestamp_t timestamp) +{ + int32 clocks = timestamp - last_ts; + + if (ReadCounter > 0) + { + ReadCounter -= clocks; + + while (ReadCounter <= 0) + { + SDR &= ~(1 << ReadBitPos); + SDR |= PadLatched & (1 << ReadBitPos); + + ReadBitPos++; + if (ReadBitPos < 16) + ReadCounter += 640; + else + { + //printf("Read End: %d\n", timestamp); + if (!(SCR & SCR_K_INT_INH)) + { + //printf("Input IRQ: %d\n", timestamp); + IntPending = true; + VBIRQ_Assert(VBIRQ_SOURCE_INPUT, IntPending); + } + break; + } + } + } + + last_ts = timestamp; + + return ((ReadCounter > 0) ? (timestamp + ReadCounter) : VB_EVENT_NONONO); +} + +void VBINPUT_ResetTS(void) +{ + last_ts = 0; +} + +void VBINPUT_Power(void) +{ + last_ts = 0; + PadData = 0; + PadLatched = 0; + SDR = 0; + SCR = 0; + ReadBitPos = 0; + ReadCounter = 0; + IntPending = false; + + VBIRQ_Assert(VBIRQ_SOURCE_INPUT, 0); +} +} diff --git a/waterbox/vb/input.h b/waterbox/vb/input.h new file mode 100644 index 0000000000..1ee57c19d7 --- /dev/null +++ b/waterbox/vb/input.h @@ -0,0 +1,45 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* input.h: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __VB_INPUT_H +#define __VB_INPUT_H + +namespace MDFN_IEN_VB +{ + +void VBINPUT_Init(void) MDFN_COLD; +void VBINPUT_SetInstantReadHack(bool); + +void VBINPUT_SetInput(unsigned port, const char *type, uint8 *ptr); + +uint8 VBINPUT_Read(v810_timestamp_t ×tamp, uint32 A); + +void VBINPUT_Write(v810_timestamp_t ×tamp, uint32 A, uint8 V); + +void VBINPUT_Frame(const void* ptr); + +int32 VBINPUT_Update(const int32 timestamp); +void VBINPUT_ResetTS(void); + + +void VBINPUT_Power(void); +} +#endif diff --git a/waterbox/vb/math_ops.h b/waterbox/vb/math_ops.h new file mode 100644 index 0000000000..4154f2d49b --- /dev/null +++ b/waterbox/vb/math_ops.h @@ -0,0 +1,278 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* math_ops.h: +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* +** Some ideas from: +** blargg +** http://graphics.stanford.edu/~seander/bithacks.html +*/ + +#ifndef __MDFN_MATH_OPS_H +#define __MDFN_MATH_OPS_H + +#if defined(_MSC_VER) + #include +#endif + +static INLINE unsigned MDFN_lzcount16_0UD(uint16 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return 15 ^ 31 ^ __builtin_clz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanReverse(&idx, v); + + return 15 ^ idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFF00) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF000) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC000) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x8000) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_lzcount32_0UD(uint32 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_clz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanReverse(&idx, v); + + return 31 ^ idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFFFF0000) << 4; v <<= tmp; ret += tmp; + tmp = !(v & 0xFF000000) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF0000000) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC0000000) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x80000000) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_lzcount64_0UD(uint64 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_clzll(v); + #elif defined(_MSC_VER) + #if defined(_WIN64) + unsigned long idx; + _BitScanReverse64(&idx, v); + return 63 ^ idx; + #else + unsigned long idx0; + unsigned long idx1; + + _BitScanReverse(&idx1, v >> 0); + idx1 -= 32; + if(!_BitScanReverse(&idx0, v >> 32)) + idx0 = idx1; + + idx0 += 32; + + return 63 ^ idx0; + #endif + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFFFFFFFF00000000ULL) << 5; v <<= tmp; ret += tmp; + tmp = !(v & 0xFFFF000000000000ULL) << 4; v <<= tmp; ret += tmp; + tmp = !(v & 0xFF00000000000000ULL) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF000000000000000ULL) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC000000000000000ULL) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x8000000000000000ULL) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_tzcount16_0UD(uint16 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanForward(&idx, v); + + return idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +static INLINE unsigned MDFN_tzcount32_0UD(uint32 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanForward(&idx, v); + + return idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !((uint16)v) << 4; v >>= tmp; ret += tmp; + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +static INLINE unsigned MDFN_tzcount64_0UD(uint64 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctzll(v); + #elif defined(_MSC_VER) + #if defined(_WIN64) + unsigned long idx; + _BitScanForward64(&idx, v); + return idx; + #else + unsigned long idx0, idx1; + + _BitScanForward(&idx1, v >> 32); + idx1 += 32; + if(!_BitScanForward(&idx0, v)) + idx0 = idx1; + + return idx0; + #endif + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !((uint32)v) << 5; v >>= tmp; ret += tmp; + tmp = !((uint16)v) << 4; v >>= tmp; ret += tmp; + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +// +// Result is defined for all possible inputs(including 0). +// +static INLINE unsigned MDFN_lzcount16(uint16 v) { return !v ? 16 : MDFN_lzcount16_0UD(v); } +static INLINE unsigned MDFN_lzcount32(uint32 v) { return !v ? 32 : MDFN_lzcount32_0UD(v); } +static INLINE unsigned MDFN_lzcount64(uint64 v) { return !v ? 64 : MDFN_lzcount64_0UD(v); } + +static INLINE unsigned MDFN_tzcount16(uint16 v) { return !v ? 16 : MDFN_tzcount16_0UD(v); } +static INLINE unsigned MDFN_tzcount32(uint32 v) { return !v ? 32 : MDFN_tzcount32_0UD(v); } +static INLINE unsigned MDFN_tzcount64(uint64 v) { return !v ? 64 : MDFN_tzcount64_0UD(v); } + +static INLINE unsigned MDFN_log2(uint32 v) { return 31 ^ MDFN_lzcount32_0UD(v | 1); } +static INLINE unsigned MDFN_log2(uint64 v) { return 63 ^ MDFN_lzcount64_0UD(v | 1); } + +static INLINE unsigned MDFN_log2(int32 v) { return MDFN_log2((uint32)v); } +static INLINE unsigned MDFN_log2(int64 v) { return MDFN_log2((uint64)v); } + +// Rounds up to the nearest power of 2(treats input as unsigned to a degree, but be aware of integer promotion rules). +// Returns 0 on overflow. +static INLINE uint64 round_up_pow2(uint32 v) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (tmp < v); } +static INLINE uint64 round_up_pow2(uint64 v) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (tmp < v); } + +static INLINE uint64 round_up_pow2(int32 v) { return round_up_pow2((uint32)v); } +static INLINE uint64 round_up_pow2(int64 v) { return round_up_pow2((uint64)v); } + +// Rounds to the nearest power of 2(treats input as unsigned to a degree, but be aware of integer promotion rules). +static INLINE uint64 round_nearest_pow2(uint32 v, bool round_half_up = true) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (v && (((v - tmp) << 1) >= (tmp + !round_half_up))); } +static INLINE uint64 round_nearest_pow2(uint64 v, bool round_half_up = true) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (v && (((v - tmp) << 1) >= (tmp + !round_half_up))); } + +static INLINE uint64 round_nearest_pow2(int32 v, bool round_half_up = true) { return round_nearest_pow2((uint32)v, round_half_up); } +static INLINE uint64 round_nearest_pow2(int64 v, bool round_half_up = true) { return round_nearest_pow2((uint64)v, round_half_up); } + +// Some compilers' optimizers and some platforms might fubar the generated code from these macros, +// so some tests are run in...tests.cpp +#define sign_8_to_s16(_value) ((int16)(int8)(_value)) +#define sign_9_to_s16(_value) (((int16)((unsigned int)(_value) << 7)) >> 7) +#define sign_10_to_s16(_value) (((int16)((uint32)(_value) << 6)) >> 6) +#define sign_11_to_s16(_value) (((int16)((uint32)(_value) << 5)) >> 5) +#define sign_12_to_s16(_value) (((int16)((uint32)(_value) << 4)) >> 4) +#define sign_13_to_s16(_value) (((int16)((uint32)(_value) << 3)) >> 3) +#define sign_14_to_s16(_value) (((int16)((uint32)(_value) << 2)) >> 2) +#define sign_15_to_s16(_value) (((int16)((uint32)(_value) << 1)) >> 1) + +// This obviously won't convert higher-than-32 bit numbers to signed 32-bit ;) +// Also, this shouldn't be used for 8-bit and 16-bit signed numbers, since you can +// convert those faster with typecasts... +#define sign_x_to_s32(_bits, _value) (((int32)((uint32)(_value) << (32 - _bits))) >> (32 - _bits)) + +static INLINE int32 clamp_to_u8(int32 i) +{ + if(i & 0xFFFFFF00) + i = (((~i) >> 30) & 0xFF); + + return(i); +} + +static INLINE int32 clamp_to_u16(int32 i) +{ + if(i & 0xFFFF0000) + i = (((~i) >> 31) & 0xFFFF); + + return(i); +} + +template static INLINE void clamp(T *val, U minimum, V maximum) +{ + if(*val < minimum) + { + //printf("Warning: clamping to minimum(%d)\n", (int)minimum); + *val = minimum; + } + if(*val > maximum) + { + //printf("Warning: clamping to maximum(%d)\n", (int)maximum); + *val = maximum; + } +} + +#endif diff --git a/waterbox/vb/timer.cpp b/waterbox/vb/timer.cpp new file mode 100644 index 0000000000..43cfe5fd45 --- /dev/null +++ b/waterbox/vb/timer.cpp @@ -0,0 +1,233 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* timer.cpp: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" +#include "timer.h" + +namespace MDFN_IEN_VB +{ + +#define TC_TENABLE 0x01 +#define TC_ZSTAT 0x02 +#define TC_ZSTATCLR 0x04 +#define TC_TIMZINT 0x08 +#define TC_TCLKSEL 0x10 + +static uint8 TimerControl; +static uint16 TimerReloadValue; +static uint16 TimerCounter; +static int32 TimerDivider; +static v810_timestamp_t TimerLastTS; +static bool TimerStatus, TimerStatusShadow; +static bool ReloadPending; + +v810_timestamp_t TIMER_Update(v810_timestamp_t timestamp) +{ + int32 run_time = timestamp - TimerLastTS; + + if (TimerControl & TC_TENABLE) + { + TimerDivider -= run_time; + while (TimerDivider <= 0) + { + if (!TimerCounter || ReloadPending) + { + TimerCounter = TimerReloadValue; + ReloadPending = false; + } + + if (TimerCounter) + TimerCounter--; + + if (!TimerCounter || TimerStatus) + { + TimerStatusShadow = TimerStatus = true; + } + + VBIRQ_Assert(VBIRQ_SOURCE_TIMER, TimerStatusShadow && (TimerControl & TC_TIMZINT)); + TimerDivider += (TimerControl & TC_TCLKSEL) ? 500 : 2000; + } + } + + TimerLastTS = timestamp; + + return ((TimerControl & TC_TENABLE) ? (timestamp + TimerDivider) : VB_EVENT_NONONO); +} + +void TIMER_ResetTS(void) +{ + TimerLastTS = 0; +} + +uint8 TIMER_Read(const v810_timestamp_t ×tamp, uint32 A) +{ + uint8 ret = 0; + + //if(A <= 0x1C) + //printf("Read: %d, %08x\n", timestamp, A); + TIMER_Update(timestamp); + + switch (A & 0xFF) + { + case 0x18: + ret = TimerCounter; + break; + + case 0x1C: + ret = TimerCounter >> 8; + break; + + case 0x20: + ret = TimerControl | (0xE0 | TC_ZSTATCLR) | (TimerStatus ? TC_ZSTAT : 0); + break; + } + + return (ret); +} + +void TIMER_Write(const v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + if (A & 0x3) + { + puts("HWCtrl Bogus Write?"); + return; + } + + TIMER_Update(timestamp); + + //if((A & 0xFF) <= 0x1C) + //printf("Write: %d, %08x %02x\n", timestamp, A, V); + + switch (A & 0xFF) + { + case 0x18: + TimerReloadValue &= 0xFF00; + TimerReloadValue |= V; + ReloadPending = true; + break; + + case 0x1C: + TimerReloadValue &= 0x00FF; + TimerReloadValue |= V << 8; + ReloadPending = true; + break; + + case 0x20: + if (V & TC_ZSTATCLR) + { + if ((TimerControl & TC_TENABLE) && TimerCounter == 0) + { + //puts("Faulty Z-Stat-Clr"); + } + else + { + TimerStatus = false; + } + TimerStatusShadow = false; + } + if ((V & TC_TENABLE) && !(TimerControl & TC_TENABLE)) + { + //TimerCounter = TimerReloadValue; + TimerDivider = (V & TC_TCLKSEL) ? 500 : 2000; + } + TimerControl = V & (0x10 | 0x08 | 0x01); + + if (!(TimerControl & TC_TIMZINT)) + TimerStatus = TimerStatusShadow = false; + + VBIRQ_Assert(VBIRQ_SOURCE_TIMER, TimerStatusShadow && (TimerControl & TC_TIMZINT)); + + if (TimerControl & TC_TENABLE) + VB_SetEvent(VB_EVENT_TIMER, timestamp + TimerDivider); + break; + } +} + +void TIMER_Power(void) +{ + TimerLastTS = 0; + + TimerCounter = 0xFFFF; + TimerReloadValue = 0; + TimerDivider = 2000; //2150; //2000; + + TimerStatus = false; + TimerStatusShadow = false; + TimerControl = 0; + + ReloadPending = false; + + VBIRQ_Assert(VBIRQ_SOURCE_TIMER, false); +} + +uint32 TIMER_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 ret = 0xDEADBEEF; + + switch (id) + { + case TIMER_GSREG_TCR: + ret = TimerControl; + if (special) + trio_snprintf(special, special_len, "TEnable: %d, TimZInt: %d, TClkSel: %d(%.3f KHz)", + (int)(bool)(ret & TC_TENABLE), + (int)(bool)(ret & TC_TIMZINT), + (int)(bool)(ret & TC_TCLKSEL), + (double)VB_MASTER_CLOCK / ((ret & TC_TCLKSEL) ? 500 : 2000) / 1000); + break; + + case TIMER_GSREG_DIVCOUNTER: + ret = TimerDivider; + break; + + case TIMER_GSREG_RELOAD_VALUE: + ret = TimerReloadValue; + break; + + case TIMER_GSREG_COUNTER: + ret = TimerCounter; + break; + } + return (ret); +} + +void TIMER_SetRegister(const unsigned int id, const uint32 value) +{ + switch (id) + { + case TIMER_GSREG_TCR: + TimerControl = value & (TC_TENABLE | TC_TIMZINT | TC_TCLKSEL); + break; + + case TIMER_GSREG_DIVCOUNTER: + TimerDivider = value % ((TimerControl & TC_TCLKSEL) ? 500 : 2000); + break; + + case TIMER_GSREG_RELOAD_VALUE: + TimerReloadValue = value; + break; + + case TIMER_GSREG_COUNTER: + TimerCounter = value; + break; + } +} +} diff --git a/waterbox/vb/timer.h b/waterbox/vb/timer.h new file mode 100644 index 0000000000..20f0f7e9b6 --- /dev/null +++ b/waterbox/vb/timer.h @@ -0,0 +1,48 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* timer.h: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MDFN_VB_TIMER_H +#define __MDFN_VB_TIMER_H + +namespace MDFN_IEN_VB +{ + +v810_timestamp_t TIMER_Update(v810_timestamp_t timestamp); +void TIMER_ResetTS(void); +uint8 TIMER_Read(const v810_timestamp_t ×tamp, uint32 A); +void TIMER_Write(const v810_timestamp_t ×tamp, uint32 A, uint8 V); + +void TIMER_Power(void) MDFN_COLD; + +enum +{ + TIMER_GSREG_TCR, + TIMER_GSREG_DIVCOUNTER, + TIMER_GSREG_RELOAD_VALUE, + TIMER_GSREG_COUNTER, +}; + +uint32 TIMER_GetRegister(const unsigned int id, char *special, const uint32 special_len); +void TIMER_SetRegister(const unsigned int id, const uint32 value); + +} + +#endif diff --git a/waterbox/vb/v810/v810_cpu.cpp b/waterbox/vb/v810/v810_cpu.cpp new file mode 100644 index 0000000000..617bc3fc0f --- /dev/null +++ b/waterbox/vb/v810/v810_cpu.cpp @@ -0,0 +1,1369 @@ +/* V810 Emulator + * + * Copyright (C) 2006 David Tucker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Alternatively, the V810 emulator code(and all V810 emulation header files) can be used/distributed under the following license(you can adopt either + license exclusively for your changes by removing one of these license headers, but it's STRONGLY preferable + to keep your changes dual-licensed as well): + +This Reality Boy emulator is copyright (C) David Tucker 1997-2008, all rights +reserved. You may use this code as long as you make no money from the use of +this code and you acknowledge the original author (Me). I reserve the right to +dictate who can use this code and how (Just so you don't do something stupid +with it). + Most Importantly, this code is swap ware. If you use It send along your new +program (with code) or some other interesting tidbits you wrote, that I might be +interested in. + This code is in beta, there are bugs! I am not responsible for any damage +done to your computer, reputation, ego, dog, or family life due to the use of +this code. All source is provided as is, I make no guaranties, and am not +responsible for anything you do with the code (legal or otherwise). + Virtual Boy is a trademark of Nintendo, and V810 is a trademark of NEC. I am +in no way affiliated with either party and all information contained hear was +found freely through public domain sources. +*/ + +////////////////////////////////////////////////////////// +// CPU routines + +#include "vb.h" + +//#include "pcfx.h" +//#include "debug.h" + +#include +#include + +#include "v810_opt.h" +#include "v810_cpu.h" + +V810::V810() +{ + MemRead8 = NULL; + MemRead16 = NULL; + MemRead32 = NULL; + + IORead8 = NULL; + IORead16 = NULL; + IORead32 = NULL; + + MemWrite8 = NULL; + MemWrite16 = NULL; + MemWrite32 = NULL; + + IOWrite8 = NULL; + IOWrite16 = NULL; + IOWrite32 = NULL; + + memset(FastMap, 0, sizeof(FastMap)); + + memset(MemReadBus32, 0, sizeof(MemReadBus32)); + memset(MemWriteBus32, 0, sizeof(MemWriteBus32)); + + v810_timestamp = 0; + next_event_ts = 0x7FFFFFFF; +} + +V810::~V810() +{ + Kill(); +} + +INLINE void V810::RecalcIPendingCache(void) +{ + IPendingCache = 0; + + // Of course don't generate an interrupt if there's not one pending! + if (ilevel < 0) + return; + + // If CPU is halted because of a fatal exception, don't let an interrupt + // take us out of this halted status. + if (Halted == HALT_FATAL_EXCEPTION) + return; + + // If the NMI pending, exception pending, and/or interrupt disabled bit + // is set, don't accept any interrupts. + if (S_REG[PSW] & (PSW_NP | PSW_EP | PSW_ID)) + return; + + // If the interrupt level is lower than the interrupt enable level, don't + // accept it. + if (ilevel < (int)((S_REG[PSW] & PSW_IA) >> 16)) + return; + + IPendingCache = 0xFF; +} + +// TODO: "An interrupt that occurs during restore/dump/clear operation is internally held and is accepted after the +// operation in progress is finished. The maskable interrupt is held internally only when the EP, NP, and ID flags +// of PSW are all 0." +// +// This behavior probably doesn't have any relevance on the PC-FX, unless we're sadistic +// and try to restore cache from an interrupt acknowledge register or dump it to a register +// controlling interrupt masks... I wanna be sadistic~ + +void V810::CacheClear(v810_timestamp_t ×tamp, uint32 start, uint32 count) +{ + //printf("Cache clear: %08x %08x\n", start, count); + for (uint32 i = 0; i < count && (i + start) < 128; i++) + memset(&Cache[i + start], 0, sizeof(V810_CacheEntry_t)); +} + +INLINE void V810::CacheOpMemStore(v810_timestamp_t ×tamp, uint32 A, uint32 V) +{ + if (MemWriteBus32[A >> 24]) + { + timestamp += 2; + MemWrite32(timestamp, A, V); + } + else + { + timestamp += 2; + MemWrite16(timestamp, A, V & 0xFFFF); + + timestamp += 2; + MemWrite16(timestamp, A | 2, V >> 16); + } +} + +INLINE uint32 V810::CacheOpMemLoad(v810_timestamp_t ×tamp, uint32 A) +{ + if (MemReadBus32[A >> 24]) + { + timestamp += 2; + return (MemRead32(timestamp, A)); + } + else + { + uint32 ret; + + timestamp += 2; + ret = MemRead16(timestamp, A); + + timestamp += 2; + ret |= MemRead16(timestamp, A | 2) << 16; + return (ret); + } +} + +void V810::CacheDump(v810_timestamp_t ×tamp, const uint32 SA) +{ + printf("Cache dump: %08x\n", SA); + + for (int i = 0; i < 128; i++) + { + CacheOpMemStore(timestamp, SA + i * 8 + 0, Cache[i].data[0]); + CacheOpMemStore(timestamp, SA + i * 8 + 4, Cache[i].data[1]); + } + + for (int i = 0; i < 128; i++) + { + uint32 icht = Cache[i].tag | ((int)Cache[i].data_valid[0] << 22) | ((int)Cache[i].data_valid[1] << 23); + + CacheOpMemStore(timestamp, SA + 1024 + i * 4, icht); + } +} + +void V810::CacheRestore(v810_timestamp_t ×tamp, const uint32 SA) +{ + printf("Cache restore: %08x\n", SA); + + for (int i = 0; i < 128; i++) + { + Cache[i].data[0] = CacheOpMemLoad(timestamp, SA + i * 8 + 0); + Cache[i].data[1] = CacheOpMemLoad(timestamp, SA + i * 8 + 4); + } + + for (int i = 0; i < 128; i++) + { + uint32 icht; + + icht = CacheOpMemLoad(timestamp, SA + 1024 + i * 4); + + Cache[i].tag = icht & ((1 << 22) - 1); + Cache[i].data_valid[0] = (icht >> 22) & 1; + Cache[i].data_valid[1] = (icht >> 23) & 1; + } +} + +INLINE uint32 V810::RDCACHE(v810_timestamp_t ×tamp, uint32 addr) +{ + const int CI = (addr >> 3) & 0x7F; + const int SBI = (addr & 4) >> 2; + + if (Cache[CI].tag == (addr >> 10)) + { + if (!Cache[CI].data_valid[SBI]) + { + timestamp += 2; // or higher? Penalty for cache miss seems to be higher than having cache disabled. + if (MemReadBus32[addr >> 24]) + Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + else + { + timestamp++; + + uint32 tmp; + + tmp = MemRead16(timestamp, addr & ~0x3); + tmp |= MemRead16(timestamp, (addr & ~0x3) | 0x2) << 16; + + Cache[CI].data[SBI] = tmp; + } + Cache[CI].data_valid[SBI] = TRUE; + } + } + else + { + Cache[CI].tag = addr >> 10; + + timestamp += 2; // or higher? Penalty for cache miss seems to be higher than having cache disabled. + if (MemReadBus32[addr >> 24]) + Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + else + { + timestamp++; + + uint32 tmp; + + tmp = MemRead16(timestamp, addr & ~0x3); + tmp |= MemRead16(timestamp, (addr & ~0x3) | 0x2) << 16; + + Cache[CI].data[SBI] = tmp; + } + //Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + Cache[CI].data_valid[SBI] = TRUE; + Cache[CI].data_valid[SBI ^ 1] = FALSE; + } + + //{ + // // Caution: This can mess up DRAM page change penalty timings + // uint32 dummy_timestamp = 0; + // if(Cache[CI].data[SBI] != mem_rword(addr & ~0x3, dummy_timestamp)) + // { + // printf("Cache/Real Memory Mismatch: %08x %08x/%08x\n", addr & ~0x3, Cache[CI].data[SBI], mem_rword(addr & ~0x3, dummy_timestamp)); + // } + //} + + return (Cache[CI].data[SBI]); +} + +INLINE uint16 V810::RDOP(v810_timestamp_t ×tamp, uint32 addr, uint32 meow) +{ + uint16 ret; + + if (S_REG[CHCW] & 0x2) + { + uint32 d32 = RDCACHE(timestamp, addr); + ret = d32 >> ((addr & 2) * 8); + } + else + { + timestamp += meow; //++; + ret = MemRead16(timestamp, addr); + } + return (ret); +} + +#define BRANCH_ALIGN_CHECK(x) \ + { \ + if ((S_REG[CHCW] & 0x2) && (x & 0x2)) \ + { \ + ADDCLOCK(1); \ + } \ + } + +// Reinitialize the defaults in the CPU +void V810::Reset() +{ + memset(&Cache, 0, sizeof(Cache)); + + memset(P_REG, 0, sizeof(P_REG)); + memset(S_REG, 0, sizeof(S_REG)); + memset(Cache, 0, sizeof(Cache)); + + P_REG[0] = 0x00000000; + SetPC(0xFFFFFFF0); + + S_REG[ECR] = 0x0000FFF0; + S_REG[PSW] = 0x00008000; + + if (VBMode) + S_REG[PIR] = 0x00005346; + else + S_REG[PIR] = 0x00008100; + + S_REG[TKCW] = 0x000000E0; + Halted = HALT_NONE; + ilevel = -1; + + lastop = 0; + + in_bstr = FALSE; + + RecalcIPendingCache(); +} + +bool V810::Init(V810_Emu_Mode mode, bool vb_mode) +{ + EmuMode = mode; + VBMode = vb_mode; + + in_bstr = FALSE; + in_bstr_to = 0; + + if (mode == V810_EMU_MODE_FAST) + { + memset(DummyRegion, 0, V810_FAST_MAP_PSIZE); + + for (unsigned int i = V810_FAST_MAP_PSIZE; i < V810_FAST_MAP_PSIZE + V810_FAST_MAP_TRAMPOLINE_SIZE; i += 2) + { + DummyRegion[i + 0] = 0; + DummyRegion[i + 1] = 0x36 << 2; + } + + for (uint64 A = 0; A < (1ULL << 32); A += V810_FAST_MAP_PSIZE) + FastMap[A / V810_FAST_MAP_PSIZE] = DummyRegion - A; + } + + return (TRUE); +} + +void V810::Kill(void) +{ + FastMapAllocList.clear(); +} + +void V810::SetInt(int level) +{ + assert(level >= -1 && level <= 15); + + ilevel = level; + RecalcIPendingCache(); +} + +uint8 *V810::SetFastMap(uint32 addresses[], uint32 length, unsigned int num_addresses, const char *name) +{ + for (unsigned int i = 0; i < num_addresses; i++) + { + assert((addresses[i] & (V810_FAST_MAP_PSIZE - 1)) == 0); + } + assert((length & (V810_FAST_MAP_PSIZE - 1)) == 0); + + FastMapAllocList.emplace_back(std::unique_ptr(new uint8[length + V810_FAST_MAP_TRAMPOLINE_SIZE])); + uint8 *ret = FastMapAllocList.back().get(); + + for (unsigned int i = length; i < length + V810_FAST_MAP_TRAMPOLINE_SIZE; i += 2) + { + ret[i + 0] = 0; + ret[i + 1] = 0x36 << 2; + } + + for (unsigned int i = 0; i < num_addresses; i++) + { + for (uint64 addr = addresses[i]; addr != (uint64)addresses[i] + length; addr += V810_FAST_MAP_PSIZE) + { + //printf("%08x, %d, %s\n", addr, length, name); + + FastMap[addr / V810_FAST_MAP_PSIZE] = ret - addresses[i]; + } + } + + return ret; +} + +void V810::SetMemReadBus32(uint8 A, bool value) +{ + MemReadBus32[A] = value; +} + +void V810::SetMemWriteBus32(uint8 A, bool value) +{ + MemWriteBus32[A] = value; +} + +void V810::SetMemReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) +{ + MemRead8 = read8; + MemRead16 = read16; + MemRead32 = read32; +} + +void V810::SetMemWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) +{ + MemWrite8 = write8; + MemWrite16 = write16; + MemWrite32 = write32; +} + +void V810::SetIOReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) +{ + IORead8 = read8; + IORead16 = read16; + IORead32 = read32; +} + +void V810::SetIOWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) +{ + IOWrite8 = write8; + IOWrite16 = write16; + IOWrite32 = write32; +} + +INLINE void V810::SetFlag(uint32 n, bool condition) +{ + S_REG[PSW] &= ~n; + + if (condition) + S_REG[PSW] |= n; +} + +INLINE void V810::SetSZ(uint32 value) +{ + SetFlag(PSW_Z, !value); + SetFlag(PSW_S, value & 0x80000000); +} + +#define SetPREG(n, val) \ + { \ + P_REG[n] = val; \ + } + +INLINE void V810::SetSREG(v810_timestamp_t ×tamp, unsigned int which, uint32 value) +{ + switch (which) + { + default: // Reserved + printf("LDSR to reserved system register: 0x%02x : 0x%08x\n", which, value); + break; + + case ECR: // Read-only + break; + + case PIR: // Read-only (obviously) + break; + + case TKCW: // Read-only + break; + + case EIPSW: + case FEPSW: + S_REG[which] = value & 0xFF3FF; + break; + + case PSW: + S_REG[which] = value & 0xFF3FF; + RecalcIPendingCache(); + break; + + case EIPC: + case FEPC: + S_REG[which] = value & 0xFFFFFFFE; + break; + + case ADDTRE: + S_REG[ADDTRE] = value & 0xFFFFFFFE; + printf("Address trap(unemulated): %08x\n", value); + break; + + case CHCW: + S_REG[CHCW] = value & 0x2; + + switch (value & 0x31) + { + default: + printf("Undefined cache control bit combination: %08x\n", value); + break; + + case 0x00: + break; + + case 0x01: + CacheClear(timestamp, (value >> 20) & 0xFFF, (value >> 8) & 0xFFF); + break; + + case 0x10: + CacheDump(timestamp, value & ~0xFF); + break; + + case 0x20: + CacheRestore(timestamp, value & ~0xFF); + break; + } + break; + } +} + +INLINE uint32 V810::GetSREG(unsigned int which) +{ + uint32 ret; + + if (which != 24 && which != 25 && which >= 8) + { + printf("STSR from reserved system register: 0x%02x", which); + } + + ret = S_REG[which]; + + return (ret); +} + +#define RB_SETPC(new_pc_raw) \ + { \ + const uint32 new_pc = new_pc_raw; /* So RB_SETPC(RB_GETPC()) won't mess up */ \ + if (RB_AccurateMode) \ + PC = new_pc; \ + else \ + { \ + PC_ptr = &FastMap[(new_pc) >> V810_FAST_MAP_SHIFT][(new_pc)]; \ + PC_base = PC_ptr - (new_pc); \ + } \ + } + +#define RB_PCRELCHANGE(delta) \ + { \ + if (RB_AccurateMode) \ + PC += (delta); \ + else \ + { \ + uint32 PC_tmp = RB_GETPC(); \ + PC_tmp += (delta); \ + RB_SETPC(PC_tmp); \ + } \ + } + +#define RB_INCPCBY2() \ + { \ + if (RB_AccurateMode) \ + PC += 2; \ + else \ + PC_ptr += 2; \ + } +#define RB_INCPCBY4() \ + { \ + if (RB_AccurateMode) \ + PC += 4; \ + else \ + PC_ptr += 4; \ + } + +#define RB_DECPCBY2() \ + { \ + if (RB_AccurateMode) \ + PC -= 2; \ + else \ + PC_ptr -= 2; \ + } +#define RB_DECPCBY4() \ + { \ + if (RB_AccurateMode) \ + PC -= 4; \ + else \ + PC_ptr -= 4; \ + } + +// Define accurate mode defines +#define RB_GETPC() PC +#define RB_RDOP(PC_offset, ...) RDOP(timestamp, PC + PC_offset, ##__VA_ARGS__) + +void V810::Run_Accurate(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = true; + +#define RB_ADDBT(n, o, p) +#define RB_CPUHOOK(n) + +#include "v810_oploop.inc" + +#undef RB_CPUHOOK +#undef RB_ADDBT +} + +// +// Undefine accurate mode defines +// +#undef RB_GETPC +#undef RB_RDOP + +// +// Define fast mode defines +// +#define RB_GETPC() ((uint32)(PC_ptr - PC_base)) + +#define RB_RDOP(PC_offset, ...) MDFN_de16lsb(&PC_ptr[PC_offset]) + +void V810::Run_Fast(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = false; + +#define RB_ADDBT(n, o, p) +#define RB_CPUHOOK(n) + +#include "v810_oploop.inc" + +#undef RB_CPUHOOK +#undef RB_ADDBT +} + +// +// Undefine fast mode defines +// +#undef RB_GETPC +#undef RB_RDOP + +v810_timestamp_t V810::Run(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + Running = true; + if (EmuMode == V810_EMU_MODE_FAST) + Run_Fast(event_handler); + else + Run_Accurate(event_handler); + return (v810_timestamp); +} + +void V810::Exit(void) +{ + Running = false; +} + +uint32 V810::GetRegister(unsigned int which, char *special, const uint32 special_len) +{ + if (which >= GSREG_PR && which <= GSREG_PR + 31) + { + return GetPR(which - GSREG_PR); + } + else if (which >= GSREG_SR && which <= GSREG_SR + 31) + { + uint32 val = GetSREG(which - GSREG_SR); + + if (special && which == GSREG_SR + PSW) + { + trio_snprintf(special, special_len, "Z: %d, S: %d, OV: %d, CY: %d, ID: %d, AE: %d, EP: %d, NP: %d, IA: %2d", + (int)(bool)(val & PSW_Z), (int)(bool)(val & PSW_S), (int)(bool)(val & PSW_OV), (int)(bool)(val & PSW_CY), + (int)(bool)(val & PSW_ID), (int)(bool)(val & PSW_AE), (int)(bool)(val & PSW_EP), (int)(bool)(val & PSW_NP), + (val & PSW_IA) >> 16); + } + + return val; + } + else if (which == GSREG_PC) + { + return GetPC(); + } + else if (which == GSREG_TIMESTAMP) + { + return v810_timestamp; + } + + return 0xDEADBEEF; +} + +void V810::SetRegister(unsigned int which, uint32 value) +{ + if (which >= GSREG_PR && which <= GSREG_PR + 31) + { + if (which) + P_REG[which - GSREG_PR] = value; + } + else if (which >= GSREG_SR && which <= GSREG_SR + 31) + { + // SetSREG(timestamp, which - GSREG_SR, value); + } + else if (which == GSREG_PC) + { + SetPC(value & ~1); + } + else if (which == GSREG_TIMESTAMP) + { + //v810_timestamp = value; + } +} + +uint32 V810::GetPC(void) +{ + if (EmuMode == V810_EMU_MODE_ACCURATE) + return (PC); + else + { + return (PC_ptr - PC_base); + } +} + +void V810::SetPC(uint32 new_pc) +{ + if (EmuMode == V810_EMU_MODE_ACCURATE) + PC = new_pc; + else + { + PC_ptr = &FastMap[new_pc >> V810_FAST_MAP_SHIFT][new_pc]; + PC_base = PC_ptr - new_pc; + } +} + +#define BSTR_OP_MOV \ + dst_cache &= ~(1 << dstoff); \ + dst_cache |= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_NOT \ + dst_cache &= ~(1 << dstoff); \ + dst_cache |= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; + +#define BSTR_OP_XOR dst_cache ^= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_OR dst_cache |= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_AND dst_cache &= ~((((src_cache >> srcoff) & 1) ^ 1) << dstoff); + +#define BSTR_OP_XORN dst_cache ^= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; +#define BSTR_OP_ORN dst_cache |= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; +#define BSTR_OP_ANDN dst_cache &= ~(((src_cache >> srcoff) & 1) << dstoff); + +INLINE uint32 V810::BSTR_RWORD(v810_timestamp_t ×tamp, uint32 A) +{ + if (MemReadBus32[A >> 24]) + { + timestamp += 2; + return (MemRead32(timestamp, A)); + } + else + { + uint32 ret; + + timestamp += 2; + ret = MemRead16(timestamp, A); + + timestamp += 2; + ret |= MemRead16(timestamp, A | 2) << 16; + return (ret); + } +} + +INLINE void V810::BSTR_WWORD(v810_timestamp_t ×tamp, uint32 A, uint32 V) +{ + if (MemWriteBus32[A >> 24]) + { + timestamp += 2; + MemWrite32(timestamp, A, V); + } + else + { + timestamp += 2; + MemWrite16(timestamp, A, V & 0xFFFF); + + timestamp += 2; + MemWrite16(timestamp, A | 2, V >> 16); + } +} + +#define DO_BSTR(op) \ + { \ + while (len) \ + { \ + if (!have_src_cache) \ + { \ + have_src_cache = TRUE; \ + src_cache = BSTR_RWORD(timestamp, src); \ + } \ + \ + if (!have_dst_cache) \ + { \ + have_dst_cache = TRUE; \ + dst_cache = BSTR_RWORD(timestamp, dst); \ + } \ + \ + op; \ + srcoff = (srcoff + 1) & 0x1F; \ + dstoff = (dstoff + 1) & 0x1F; \ + len--; \ + \ + if (!srcoff) \ + { \ + src += 4; \ + have_src_cache = FALSE; \ + } \ + \ + if (!dstoff) \ + { \ + BSTR_WWORD(timestamp, dst, dst_cache); \ + dst += 4; \ + have_dst_cache = FALSE; \ + if (timestamp >= next_event_ts) \ + break; \ + } \ + } \ + if (have_dst_cache) \ + BSTR_WWORD(timestamp, dst, dst_cache); \ + } + +INLINE bool V810::Do_BSTR_Search(v810_timestamp_t ×tamp, const int inc_mul, unsigned int bit_test) +{ + uint32 srcoff = (P_REG[27] & 0x1F); + uint32 len = P_REG[28]; + uint32 bits_skipped = P_REG[29]; + uint32 src = (P_REG[30] & 0xFFFFFFFC); + bool found = false; + +#if 0 + // TODO: Better timing. + if(!in_bstr) // If we're just starting the execution of this instruction(kind of spaghetti-code), so FIXME if we change + // bstr handling in v810_oploop.inc + { + timestamp += 13 - 1; + } +#endif + + while (len) + { + if (!have_src_cache) + { + have_src_cache = TRUE; + timestamp++; + src_cache = BSTR_RWORD(timestamp, src); + } + + if (((src_cache >> srcoff) & 1) == bit_test) + { + found = true; + + /* Fix the bit offset and word address to "1 bit before" it was found */ + srcoff -= inc_mul * 1; + if (srcoff & 0x20) /* Handles 0x1F->0x20(0x00) and 0x00->0xFFFF... */ + { + src -= inc_mul * 4; + srcoff &= 0x1F; + } + break; + } + srcoff = (srcoff + inc_mul * 1) & 0x1F; + bits_skipped++; + len--; + + if (!srcoff) + { + have_src_cache = FALSE; + src += inc_mul * 4; + if (timestamp >= next_event_ts) + break; + } + } + + P_REG[27] = srcoff; + P_REG[28] = len; + P_REG[29] = bits_skipped; + P_REG[30] = src; + + if (found) // Set Z flag to 0 if the bit was found + SetFlag(PSW_Z, 0); + else if (!len) // ...and if the search is over, and the bit was not found, set it to 1 + SetFlag(PSW_Z, 1); + + if (found) // Bit found, so don't continue the search. + return (false); + + return ((bool)len); // Continue the search if any bits are left to search. +} + +bool V810::bstr_subop(v810_timestamp_t ×tamp, int sub_op, int arg1) +{ + if ((sub_op >= 0x10) || (!(sub_op & 0x8) && sub_op >= 0x4)) + { + printf("%08x\tBSR Error: %04x\n", PC, sub_op); + + SetPC(GetPC() - 2); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + + return (false); + } + + // printf("BSTR: %02x, %02x %02x; src: %08x, dst: %08x, len: %08x\n", sub_op, P_REG[27], P_REG[26], P_REG[30], P_REG[29], P_REG[28]); + + if (sub_op & 0x08) + { + uint32 dstoff = (P_REG[26] & 0x1F); + uint32 srcoff = (P_REG[27] & 0x1F); + uint32 len = P_REG[28]; + uint32 dst = (P_REG[29] & 0xFFFFFFFC); + uint32 src = (P_REG[30] & 0xFFFFFFFC); + +#if 0 + // Be careful not to cause 32-bit integer overflow, and careful about not shifting by 32. + // TODO: + + // Read src[0], src[4] into shifter. + // Read dest[0]. + DO_BSTR_PROLOGUE(); // if(len) { blah blah blah masking blah } + src_cache = BSTR_RWORD(timestamp, src); + + if((uint64)(srcoff + len) > 0x20) + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + + dst_cache = BSTR_RWORD(timestamp, dst); + + if(len) + { + uint32 dst_preserve_mask; + uint32 dst_change_mask; + + dst_preserve_mask = (1U << dstoff) - 1; + + if((uint64)(dstoff + len) < 0x20) + dst_preserve_mask |= ((1U << ((0x20 - (dstoff + len)) & 0x1F)) - 1) << (dstoff + len); + + dst_change_mask = ~dst_preserve_mask; + + src_cache = BSTR_RWORD(timestamp, src); + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + dst_cache = BSTR_RWORD(timestamp, dst); + + dst_cache = (dst_cache & dst_preserve_mask) | ((dst_cache OP_THINGY_HERE (src_cache >> srcoff)) & dst_change_mask); + BSTR_WWORD(timestamp, dst, dst_cache); + + if((uint64)(dstoff + len) < 0x20) + { + srcoff += len; + dstoff += len; + len = 0; + } + else + { + srcoff += (0x20 - dstoff); + dstoff = 0; + len -= (0x20 - dstoff); + dst += 4; + } + + if(srcoff >= 0x20) + { + srcoff &= 0x1F; + src += 4; + + if(len) + { + src_cache >>= 32; + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + } + } + } + + DO_BSTR_PRIMARY(); // while(len >= 32) (do allow interruption; interrupt and emulator-return - + // they must be handled differently!) + while(len >= 32) + { + dst_cache = BSTR_RWORD(timestamp, dst); + dst_cache = OP_THINGY_HERE(dst_cache, src_cache >> srcoff); + BSTR_WWORD(timestamp, dst, dst_cache); + len -= 32; + dst += 4; + src += 4; + src_cache >>= 32; + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + } + + DO_BSTR_EPILOGUE(); // if(len) { blah blah blah masking blah } + if(len) + { + uint32 dst_preserve_mask; + uint32 dst_change_mask; + + dst_preserve_mask = (1U << ((0x20 - len) & 0x1F) << len; + dst_change_mask = ~dst_preserve_mask; + + dst_cache = BSTR_RWORD(timestamp, dst); + dst_cache = OP_THINGY_HERE(dst_cache, src_cache >> srcoff); + BSTR_WWORD(timestamp, dst, dst_cache); + dstoff += len; + srcoff += len; + + if(srcoff >= 0x20) + { + srcoff &= 0x1F; + src += 4; + } + len = 0; + } +#endif + + switch (sub_op) + { + case ORBSU: + DO_BSTR(BSTR_OP_OR); + break; + + case ANDBSU: + DO_BSTR(BSTR_OP_AND); + break; + + case XORBSU: + DO_BSTR(BSTR_OP_XOR); + break; + + case MOVBSU: + DO_BSTR(BSTR_OP_MOV); + break; + + case ORNBSU: + DO_BSTR(BSTR_OP_ORN); + break; + + case ANDNBSU: + DO_BSTR(BSTR_OP_ANDN); + break; + + case XORNBSU: + DO_BSTR(BSTR_OP_XORN); + break; + + case NOTBSU: + DO_BSTR(BSTR_OP_NOT); + break; + } + + P_REG[26] = dstoff; + P_REG[27] = srcoff; + P_REG[28] = len; + P_REG[29] = dst; + P_REG[30] = src; + + return ((bool)P_REG[28]); + } + else + { + printf("BSTR Search: %02x\n", sub_op); + return (Do_BSTR_Search(timestamp, ((sub_op & 1) ? -1 : 1), (sub_op & 0x2) >> 1)); + } + assert(0); + return (false); +} + +INLINE void V810::SetFPUOPNonFPUFlags(uint32 result) +{ + // Now, handle flag setting + SetFlag(PSW_OV, 0); + + if (!(result & 0x7FFFFFFF)) // Check to see if exponent and mantissa are 0 + { + // If Z flag is set, S and CY should be clear, even if it's negative 0(confirmed on real thing with subf.s, at least). + SetFlag(PSW_Z, 1); + SetFlag(PSW_S, 0); + SetFlag(PSW_CY, 0); + } + else + { + SetFlag(PSW_Z, 0); + SetFlag(PSW_S, result & 0x80000000); + SetFlag(PSW_CY, result & 0x80000000); + } + //printf("MEOW: %08x\n", S_REG[PSW] & (PSW_S | PSW_CY)); +} + +bool V810::FPU_DoesExceptionKillResult(void) +{ + const uint32 float_exception_flags = fpo.get_flags(); + + if (float_exception_flags & V810_FP_Ops::flag_reserved) + return (true); + + if (float_exception_flags & V810_FP_Ops::flag_invalid) + return (true); + + if (float_exception_flags & V810_FP_Ops::flag_divbyzero) + return (true); + + // Return false here, so that the result of this calculation IS put in the output register. + // Wrap the exponent on overflow, rather than generating an infinity. The wrapping behavior is specified in IEE 754 AFAIK, + // and is useful in cases where you divide a huge number + // by another huge number, and fix the result afterwards based on the number of overflows that occurred. Probably requires some custom assembly code, + // though. And it's the kind of thing you'd see in an engineering or physics program, not in a perverted video game :b). + if (float_exception_flags & V810_FP_Ops::flag_overflow) + return (false); + + return (false); +} + +void V810::FPU_DoException(void) +{ + const uint32 float_exception_flags = fpo.get_flags(); + + if (float_exception_flags & V810_FP_Ops::flag_reserved) + { + S_REG[PSW] |= PSW_FRO; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FRO); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_invalid) + { + S_REG[PSW] |= PSW_FIV; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FIV); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_divbyzero) + { + S_REG[PSW] |= PSW_FZD; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FZD); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_underflow) + { + S_REG[PSW] |= PSW_FUD; + } + + if (float_exception_flags & V810_FP_Ops::flag_inexact) + { + S_REG[PSW] |= PSW_FPR; + } + + // + // FPR can be set along with overflow, so put the overflow exception handling at the end here(for Exception() messes with PSW). + // + if (float_exception_flags & V810_FP_Ops::flag_overflow) + { + S_REG[PSW] |= PSW_FOV; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FOV); + } +} + +bool V810::IsSubnormal(uint32 fpval) +{ + if (((fpval >> 23) & 0xFF) == 0 && (fpval & ((1 << 23) - 1))) + return (true); + + return (false); +} + +INLINE void V810::FPU_Math_Template(uint32 (V810_FP_Ops::*func)(uint32, uint32), uint32 arg1, uint32 arg2) +{ + uint32 result; + + fpo.clear_flags(); + result = (fpo.*func)(P_REG[arg1], P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetFPUOPNonFPUFlags(result); + SetPREG(arg1, result); + } + FPU_DoException(); +} + +void V810::fpu_subop(v810_timestamp_t ×tamp, int sub_op, int arg1, int arg2) +{ + //printf("FPU: %02x\n", sub_op); + if (VBMode) + { + switch (sub_op) + { + case XB: + timestamp++; // Unknown + P_REG[arg1] = (P_REG[arg1] & 0xFFFF0000) | ((P_REG[arg1] & 0xFF) << 8) | ((P_REG[arg1] & 0xFF00) >> 8); + return; + + case XH: + timestamp++; // Unknown + P_REG[arg1] = (P_REG[arg1] << 16) | (P_REG[arg1] >> 16); + return; + + // Does REV use arg1 or arg2 for the source register? + case REV: + timestamp++; // Unknown + printf("Revvie bits\n"); + { + // Public-domain code snippet from: http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + uint32 v = P_REG[arg2]; // 32-bit word to reverse bit order + + // swap odd and even bits + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + // swap consecutive pairs + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + // swap nibbles ... + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); + // swap bytes + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); + // swap 2-byte long pairs + v = (v >> 16) | (v << 16); + + P_REG[arg1] = v; + } + return; + + case MPYHW: + timestamp += 9 - 1; // Unknown? + P_REG[arg1] = (int32)(int16)(P_REG[arg1] & 0xFFFF) * (int32)(int16)(P_REG[arg2] & 0xFFFF); + return; + } + } + + switch (sub_op) + { + // Virtual-Boy specific(probably!) + default: + { + SetPC(GetPC() - 4); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + } + break; + + case CVT_WS: + timestamp += 5; + { + uint32 result; + + fpo.clear_flags(); + result = fpo.itof(P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFPUOPNonFPUFlags(result); + } + FPU_DoException(); + } + break; // End CVT.WS + + case CVT_SW: + timestamp += 8; + { + int32 result; + + fpo.clear_flags(); + result = fpo.ftoi(P_REG[arg2], false); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFlag(PSW_OV, 0); + SetSZ(result); + } + FPU_DoException(); + } + break; // End CVT.SW + + case ADDF_S: + timestamp += 8; + FPU_Math_Template(&V810_FP_Ops::add, arg1, arg2); + break; + + case SUBF_S: + timestamp += 11; + FPU_Math_Template(&V810_FP_Ops::sub, arg1, arg2); + break; + + case CMPF_S: + timestamp += 6; + // Don't handle this like subf.s because the flags + // have slightly different semantics(mostly regarding underflow/subnormal results) (confirmed on real V810). + fpo.clear_flags(); + { + int32 result; + + result = fpo.cmp(P_REG[arg1], P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetFPUOPNonFPUFlags(result); + } + FPU_DoException(); + } + break; + + case MULF_S: + timestamp += 7; + FPU_Math_Template(&V810_FP_Ops::mul, arg1, arg2); + break; + + case DIVF_S: + timestamp += 43; + FPU_Math_Template(&V810_FP_Ops::div, arg1, arg2); + break; + + case TRNC_SW: + timestamp += 7; + { + int32 result; + + fpo.clear_flags(); + result = fpo.ftoi(P_REG[arg2], true); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFlag(PSW_OV, 0); + SetSZ(result); + } + FPU_DoException(); + } + break; // end TRNC.SW + } +} + +// Generate exception +void V810::Exception(uint32 handler, uint16 eCode) +{ +// Exception overhead is unknown. + printf("Exception: %08x %04x\n", handler, eCode); + + // Invalidate our bitstring state(forces the instruction to be re-read, and the r/w buffers reloaded). + in_bstr = FALSE; + have_src_cache = FALSE; + have_dst_cache = FALSE; + + if (S_REG[PSW] & PSW_NP) // Fatal exception + { + printf("Fatal exception; Code: %08x, ECR: %08x, PSW: %08x, PC: %08x\n", eCode, S_REG[ECR], S_REG[PSW], PC); + Halted = HALT_FATAL_EXCEPTION; + IPendingCache = 0; + return; + } + else if (S_REG[PSW] & PSW_EP) //Double Exception + { + S_REG[FEPC] = GetPC(); + S_REG[FEPSW] = S_REG[PSW]; + + S_REG[ECR] = (S_REG[ECR] & 0xFFFF) | (eCode << 16); + S_REG[PSW] |= PSW_NP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + SetPC(0xFFFFFFD0); + IPendingCache = 0; + return; + } + else // Regular exception + { + S_REG[EIPC] = GetPC(); + S_REG[EIPSW] = S_REG[PSW]; + S_REG[ECR] = (S_REG[ECR] & 0xFFFF0000) | eCode; + S_REG[PSW] |= PSW_EP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + SetPC(handler); + IPendingCache = 0; + return; + } +} diff --git a/waterbox/vb/v810/v810_cpu.h b/waterbox/vb/v810/v810_cpu.h new file mode 100644 index 0000000000..82a1d738c0 --- /dev/null +++ b/waterbox/vb/v810/v810_cpu.h @@ -0,0 +1,335 @@ +//////////////////////////////////////////////////////////////// +// Defines for the V810 CPU + +#pragma once + +#include + +typedef int32 v810_timestamp_t; + +#define V810_FAST_MAP_SHIFT 16 +#define V810_FAST_MAP_PSIZE (1 << V810_FAST_MAP_SHIFT) +#define V810_FAST_MAP_TRAMPOLINE_SIZE 1024 + +// Exception codes +enum +{ + ECODE_TRAP_BASE = 0xFFA0, + ECODE_INVALID_OP = 0xFF90, + ECODE_ZERO_DIV = 0xFF80, // Integer divide by 0 + ECODE_FIV = 0xFF70, // Floating point invalid operation + ECODE_FZD = 0xFF68, // Floating point zero division + ECODE_FOV = 0xFF64, // Floating point overflow + //#define ECODE_FUD 0xFF62 // Floating point underflow(unused on V810) + //#define ECODE_FPR 0xFF61 // Floating point precision degradation(unused on V810) + ECODE_FRO = 0xFF60 // Floating point reserved operand +}; + +enum +{ + INVALID_OP_HANDLER_ADDR = 0xFFFFFF90, // Invalid opcode/instruction code! + ZERO_DIV_HANDLER_ADDR = 0xFFFFFF80, // Integer divide by 0 exception + FPU_HANDLER_ADDR = 0xFFFFFF60, // FPU exception + TRAP_HANDLER_BASE = 0xFFFFFFA0 // TRAP instruction +}; + +//System Register Defines (these are the only valid system registers!) +#define EIPC 0 //Exeption/Interupt PC +#define EIPSW 1 //Exeption/Interupt PSW + +#define FEPC 2 //Fatal Error PC +#define FEPSW 3 //Fatal Error PSW + +#define ECR 4 //Exception Cause Register +#define PSW 5 //Program Status Word +#define PIR 6 //Processor ID Register +#define TKCW 7 //Task Controll Word +#define CHCW 24 //Cashe Controll Word +#define ADDTRE 25 //ADDTRE + +//PSW Specifics +#define PSW_IA 0xF0000 // All Interupt bits... +#define PSW_I3 0x80000 +#define PSW_I2 0x40000 +#define PSW_I1 0x20000 +#define PSW_I0 0x10000 + +#define PSW_NP 0x08000 +#define PSW_EP 0x04000 + +#define PSW_AE 0x02000 + +#define PSW_ID 0x01000 + +#define PSW_FRO 0x00200 // Floating point reserved operand(set on denormal, NaN, or indefinite) +#define PSW_FIV 0x00100 // Floating point invalid operation(set when trying to convert a number too large to an (un)signed integer) + +#define PSW_FZD 0x00080 // Floating point divide by zero +#define PSW_FOV 0x00040 // Floating point overflow +#define PSW_FUD 0x00020 // Floating point underflow +#define PSW_FPR 0x00010 // Floating point precision degradation + +#define PSW_CY 0x00008 +#define PSW_OV 0x00004 +#define PSW_S 0x00002 +#define PSW_Z 0x00001 + +//condition codes +#define COND_V 0 +#define COND_C 1 +#define COND_Z 2 +#define COND_NH 3 +#define COND_S 4 +#define COND_T 5 +#define COND_LT 6 +#define COND_LE 7 +#define COND_NV 8 +#define COND_NC 9 +#define COND_NZ 10 +#define COND_H 11 +#define COND_NS 12 +#define COND_F 13 +#define COND_GE 14 +#define COND_GT 15 + +#define TESTCOND_V (S_REG[PSW] & PSW_OV) + +#define TESTCOND_L (S_REG[PSW] & PSW_CY) +#define TESTCOND_C TESTCOND_L + +#define TESTCOND_E (S_REG[PSW] & PSW_Z) +#define TESTCOND_Z TESTCOND_E + +#define TESTCOND_NH ((S_REG[PSW] & PSW_Z) || (S_REG[PSW] & PSW_CY)) +#define TESTCOND_N (S_REG[PSW] & PSW_S) +#define TESTCOND_S TESTCOND_N + +#define TESTCOND_LT ((!!(S_REG[PSW] & PSW_S)) ^ (!!(S_REG[PSW] & PSW_OV))) +#define TESTCOND_LE (((!!(S_REG[PSW] & PSW_S)) ^ (!!(S_REG[PSW] & PSW_OV))) || (S_REG[PSW] & PSW_Z)) +#define TESTCOND_NV (!(S_REG[PSW] & PSW_OV)) + +#define TESTCOND_NL (!(S_REG[PSW] & PSW_CY)) +#define TESTCOND_NC TESTCOND_NL + +#define TESTCOND_NE (!(S_REG[PSW] & PSW_Z)) +#define TESTCOND_NZ TESTCOND_NE + +#define TESTCOND_H (!((S_REG[PSW] & PSW_Z) || (S_REG[PSW] & PSW_CY))) +#define TESTCOND_P (!(S_REG[PSW] & PSW_S)) +#define TESTCOND_NS TESTCOND_P + +#define TESTCOND_GE (!((!!(S_REG[PSW] & PSW_S)) ^ (!!(S_REG[PSW] & PSW_OV)))) +#define TESTCOND_GT (!(((!!(S_REG[PSW] & PSW_S)) ^ (!!(S_REG[PSW] & PSW_OV))) || (S_REG[PSW] & PSW_Z))) + +// Tag layout +// Bit 0-21: TAG31-TAG10 +// Bit 22-23: Validity bits(one for each 4-byte subblock) +// Bit 24-27: NECRV("Reserved") +// Bit 28-31: 0 + +typedef enum { + V810_EMU_MODE_FAST = 0, + V810_EMU_MODE_ACCURATE = 1, + _V810_EMU_MODE_COUNT +} V810_Emu_Mode; + +class V810 +{ + public: + V810() + MDFN_COLD; + ~V810() MDFN_COLD; + + // Pass TRUE for vb_mode if we're emulating a VB-specific enhanced V810 CPU core + bool Init(V810_Emu_Mode mode, bool vb_mode) MDFN_COLD; + void Kill(void) MDFN_COLD; + + void SetInt(int level); + + void SetMemWriteBus32(uint8 A, bool value) MDFN_COLD; + void SetMemReadBus32(uint8 A, bool value) MDFN_COLD; + + void SetMemReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) MDFN_COLD; + void SetMemWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) MDFN_COLD; + + void SetIOReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) MDFN_COLD; + void SetIOWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) MDFN_COLD; + + // Length specifies the number of bytes to map in, at each location specified by addresses[] (for mirroring) + uint8 *SetFastMap(uint32 addresses[], uint32 length, unsigned int num_addresses, const char *name) MDFN_COLD; + + INLINE void ResetTS(v810_timestamp_t new_base_timestamp) + { + assert(next_event_ts > v810_timestamp); + + next_event_ts -= (v810_timestamp - new_base_timestamp); + v810_timestamp = new_base_timestamp; + } + + INLINE void SetEventNT(const v810_timestamp_t timestamp) + { + next_event_ts = timestamp; + } + + INLINE v810_timestamp_t GetEventNT(void) + { + return (next_event_ts); + } + + v810_timestamp_t Run(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)); + void Exit(void); + + void Reset(void) MDFN_COLD; + + enum + { + GSREG_PR = 0, + GSREG_SR = 32, + GSREG_PC = 64, + GSREG_TIMESTAMP + }; + + uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); + void SetRegister(unsigned int which, uint32 value); + + uint32 GetPC(void); + void SetPC(uint32); + + INLINE uint32 GetPR(unsigned int which) + { + return which ? P_REG[which] : 0; + } + + private: + // Make sure P_REG[] is the first variable/array in this class, so non-zerfo offset encoding(at assembly level) isn't necessary to access it. + uint32 P_REG[32]; // Program registers pr0-pr31 + uint32 S_REG[32]; // System registers sr0-sr31 + uint32 PC; + uint8 *PC_ptr; + uint8 *PC_base; + + uint32 IPendingCache; + void RecalcIPendingCache(void); + + public: + v810_timestamp_t v810_timestamp; // Will never be less than 0. + + private: + v810_timestamp_t next_event_ts; + + enum + { + LASTOP_NORMAL = 0, + LASTOP_LOAD = 1, + LASTOP_STORE = 2, + LASTOP_IN = 3, + LASTOP_OUT = 4, + LASTOP_HEAVY_MATH = 5 + }; + + V810_Emu_Mode EmuMode; + bool VBMode; + + void Run_Fast(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + void Run_Accurate(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + + uint8 MDFN_FASTCALL (*MemRead8)(v810_timestamp_t ×tamp, uint32 A); + uint16 MDFN_FASTCALL (*MemRead16)(v810_timestamp_t ×tamp, uint32 A); + uint32 MDFN_FASTCALL (*MemRead32)(v810_timestamp_t ×tamp, uint32 A); + + void MDFN_FASTCALL (*MemWrite8)(v810_timestamp_t ×tamp, uint32 A, uint8 V); + void MDFN_FASTCALL (*MemWrite16)(v810_timestamp_t ×tamp, uint32 A, uint16 V); + void MDFN_FASTCALL (*MemWrite32)(v810_timestamp_t ×tamp, uint32 A, uint32 V); + + uint8 MDFN_FASTCALL (*IORead8)(v810_timestamp_t ×tamp, uint32 A); + uint16 MDFN_FASTCALL (*IORead16)(v810_timestamp_t ×tamp, uint32 A); + uint32 MDFN_FASTCALL (*IORead32)(v810_timestamp_t ×tamp, uint32 A); + + void MDFN_FASTCALL (*IOWrite8)(v810_timestamp_t ×tamp, uint32 A, uint8 V); + void MDFN_FASTCALL (*IOWrite16)(v810_timestamp_t ×tamp, uint32 A, uint16 V); + void MDFN_FASTCALL (*IOWrite32)(v810_timestamp_t ×tamp, uint32 A, uint32 V); + + bool MemReadBus32[256]; // Corresponding to the upper 8 bits of the memory address map. + bool MemWriteBus32[256]; + + int32 lastop; // Set to -1 on FP/MUL/DIV, 0x100 on LD, 0x200 on ST, 0x400 on in, 0x800 on out, and the actual opcode * 2(or >= 0) on everything else. + +#define LASTOP_LD 0x100 +#define LASTOP_ST 0x200 +#define LASTOP_IN 0x400 +#define LASTOP_OUT 0x800 + + enum + { + HALT_NONE = 0, + HALT_HALT = 1, + HALT_FATAL_EXCEPTION = 2 + }; + + uint8 Halted; + + bool Running; + + int ilevel; + + bool in_bstr; + uint16 in_bstr_to; + + bool bstr_subop(v810_timestamp_t ×tamp, int sub_op, int arg1); + void fpu_subop(v810_timestamp_t ×tamp, int sub_op, int arg1, int arg2); + + void Exception(uint32 handler, uint16 eCode); + + // Caching-related: + typedef struct + { + uint32 tag; + uint32 data[2]; + bool data_valid[2]; + } V810_CacheEntry_t; + + V810_CacheEntry_t Cache[128]; + + // Bitstring variables. + uint32 src_cache; + uint32 dst_cache; + bool have_src_cache, have_dst_cache; + + uint8 *FastMap[(1ULL << 32) / V810_FAST_MAP_PSIZE]; + std::vector> FastMapAllocList; + + // For CacheDump and CacheRestore + void CacheOpMemStore(v810_timestamp_t ×tamp, uint32 A, uint32 V); + uint32 CacheOpMemLoad(v810_timestamp_t ×tamp, uint32 A); + + void CacheClear(v810_timestamp_t ×tamp, uint32 start, uint32 count); + void CacheDump(v810_timestamp_t ×tamp, const uint32 SA); + void CacheRestore(v810_timestamp_t ×tamp, const uint32 SA); + + uint32 RDCACHE(v810_timestamp_t ×tamp, uint32 addr); + // + // End caching related + // + + uint16 RDOP(v810_timestamp_t ×tamp, uint32 addr, uint32 meow = 2); + void SetFlag(uint32 n, bool condition); + void SetSZ(uint32 value); + + void SetSREG(v810_timestamp_t ×tamp, unsigned int which, uint32 value); + uint32 GetSREG(unsigned int which); + + bool IsSubnormal(uint32 fpval); + void FPU_Math_Template(uint32 (V810_FP_Ops::*func)(uint32, uint32), uint32 arg1, uint32 arg2); + void FPU_DoException(void); + bool CheckFPInputException(uint32 fpval); + bool FPU_DoesExceptionKillResult(void); + void SetFPUOPNonFPUFlags(uint32 result); + + uint32 BSTR_RWORD(v810_timestamp_t ×tamp, uint32 A); + void BSTR_WWORD(v810_timestamp_t ×tamp, uint32 A, uint32 V); + bool Do_BSTR_Search(v810_timestamp_t ×tamp, const int inc_mul, unsigned int bit_test); + + V810_FP_Ops fpo; + + uint8 DummyRegion[V810_FAST_MAP_PSIZE + V810_FAST_MAP_TRAMPOLINE_SIZE]; +}; diff --git a/waterbox/vb/v810/v810_do_am.h b/waterbox/vb/v810/v810_do_am.h new file mode 100644 index 0000000000..9cc01568d3 --- /dev/null +++ b/waterbox/vb/v810/v810_do_am.h @@ -0,0 +1,72 @@ +#define DO_MOV_AM(); DO_AM_I(); +#define DO_ADD_AM(); DO_AM_I(); +#define DO_SUB_AM(); DO_AM_I(); +#define DO_CMP_AM(); DO_AM_I(); +#define DO_SHL_AM(); DO_AM_I(); +#define DO_SHR_AM(); DO_AM_I(); +#define DO_JMP_AM(); DO_AM_I(); +#define DO_SAR_AM(); DO_AM_I(); +#define DO_MUL_AM(); DO_AM_I(); +#define DO_DIV_AM(); DO_AM_I(); +#define DO_MULU_AM(); DO_AM_I(); +#define DO_DIVU_AM(); DO_AM_I(); +#define DO_OR_AM(); DO_AM_I(); +#define DO_AND_AM(); DO_AM_I(); +#define DO_XOR_AM(); DO_AM_I(); +#define DO_NOT_AM(); DO_AM_I(); +#define DO_MOV_I_AM(); DO_AM_II(); +#define DO_ADD_I_AM(); DO_AM_II(); +#define DO_SETF_AM(); DO_AM_II(); +#define DO_CMP_I_AM(); DO_AM_II(); +#define DO_SHL_I_AM(); DO_AM_II(); +#define DO_SHR_I_AM(); DO_AM_II(); +#define DO_EI_AM(); DO_AM_II(); +#define DO_SAR_I_AM(); DO_AM_II(); +#define DO_TRAP_AM(); DO_AM_II(); +#define DO_RETI_AM(); DO_AM_IX(); +#define DO_HALT_AM(); DO_AM_IX(); +#define DO_LDSR_AM(); DO_AM_II(); +#define DO_STSR_AM(); DO_AM_II(); +#define DO_DI_AM(); DO_AM_II(); +#define DO_BSTR_AM(); DO_AM_BSTR(); +#define DO_MOVEA_AM(); DO_AM_V(); +#define DO_ADDI_AM(); DO_AM_V(); +#define DO_JR_AM(); DO_AM_IV(); +#define DO_JAL_AM(); DO_AM_IV(); +#define DO_ORI_AM(); DO_AM_V(); +#define DO_ANDI_AM(); DO_AM_V(); +#define DO_XORI_AM(); DO_AM_V(); +#define DO_MOVHI_AM(); DO_AM_V(); +#define DO_LD_B_AM(); DO_AM_VIa(); +#define DO_LD_H_AM(); DO_AM_VIa(); +#define DO_LD_W_AM(); DO_AM_VIa(); +#define DO_ST_B_AM(); DO_AM_VIb(); +#define DO_ST_H_AM(); DO_AM_VIb(); +#define DO_ST_W_AM(); DO_AM_VIb(); +#define DO_IN_B_AM(); DO_AM_VIa(); +#define DO_IN_H_AM(); DO_AM_VIa(); +#define DO_CAXI_AM(); DO_AM_VIa(); +#define DO_IN_W_AM(); DO_AM_VIa(); +#define DO_OUT_B_AM(); DO_AM_VIb(); +#define DO_OUT_H_AM(); DO_AM_VIb(); +#define DO_FPP_AM(); DO_AM_FPP(); +#define DO_OUT_W_AM(); DO_AM_VIb(); +#define DO_BV_AM(); DO_AM_III(); +#define DO_BL_AM(); DO_AM_III(); +#define DO_BE_AM(); DO_AM_III(); +#define DO_BNH_AM(); DO_AM_III(); +#define DO_BN_AM(); DO_AM_III(); +#define DO_BR_AM(); DO_AM_III(); +#define DO_BLT_AM(); DO_AM_III(); +#define DO_BLE_AM(); DO_AM_III(); +#define DO_BNV_AM(); DO_AM_III(); +#define DO_BNL_AM(); DO_AM_III(); +#define DO_BNE_AM(); DO_AM_III(); +#define DO_BH_AM(); DO_AM_III(); +#define DO_BP_AM(); DO_AM_III(); +#define DO_NOP_AM(); DO_AM_III(); +#define DO_BGE_AM(); DO_AM_III(); +#define DO_BGT_AM(); DO_AM_III(); + + +#define DO_INVALID_AM(); DO_AM_UDEF(); diff --git a/waterbox/vb/v810/v810_fp_ops.cpp b/waterbox/vb/v810/v810_fp_ops.cpp new file mode 100644 index 0000000000..53aa937fae --- /dev/null +++ b/waterbox/vb/v810/v810_fp_ops.cpp @@ -0,0 +1,405 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* v810_fp_ops.cpp: +** Copyright (C) 2014-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" + +bool V810_FP_Ops::fp_is_zero(uint32 v) +{ + return ((v & 0x7FFFFFFF) == 0); +} + +#if 0 +bool V810_FP_Ops::fp_is_nan(uint32 v) +{ + return((v & 0x7FFFFFFF) > (255 << 23)); +} + +bool V810_FP_Ops::fp_is_inf(uint32 v) +{ + return((v & 0x7FFFFFFF) == (255 << 23)); +} +#endif + +bool V810_FP_Ops::fp_is_inf_nan_sub(uint32 v) +{ + if ((v & 0x7FFFFFFF) == 0) + return (false); + + switch ((v >> 23) & 0xFF) + { + case 0x00: + case 0xff: + return (true); + } + return (false); +} + +void V810_FP_Ops::fpim_decode(fpim *df, uint32 v) +{ + df->exp = ((v >> 23) & 0xFF) - 127; + df->f = (v & 0x7FFFFF) | ((v & 0x7FFFFFFF) ? 0x800000 : 0); + df->sign = v >> 31; +} + +void V810_FP_Ops::fpim_round(fpim *df) +{ + int vbc = 64 - MDFN_lzcount64(df->f); + + if (vbc > 24) + { + const unsigned sa = vbc - 24; + + if (1) // round to nearest + { + uint64 old_f = df->f; + + df->f = (df->f + ((df->f >> sa) & 1) + ((1ULL << (sa - 1)) - 1)) & ~((1ULL << sa) - 1); + + if (df->f != old_f) + { + //printf("Inexact mr\n"); + exception_flags |= flag_inexact; + } + } + else + abort(); + } +} + +void V810_FP_Ops::fpim_round_int(fpim *df, bool truncate) +{ + if (df->exp < 23) + { + const unsigned sa = 23 - df->exp; + uint64 old_f = df->f; + + //if(sa >= 2) + // printf("RI: %lld, %d\n", df->f, sa); + + // round to nearest + if (sa > 24) + df->f = 0; + else + { + if (truncate) + df->f = df->f & ~((1ULL << sa) - 1); + else + df->f = (df->f + ((df->f >> sa) & 1) + ((1ULL << (sa - 1)) - 1)) & ~((1ULL << sa) - 1); + } + + if (df->f != old_f) + { + //printf("Inexact\n"); + exception_flags |= flag_inexact; + } + } +} + +uint32 V810_FP_Ops::fpim_encode(fpim *df) +{ + const int lzc = MDFN_lzcount64(df->f); + int tmp_exp = df->exp - lzc; + uint64 tmp_walrus = df->f << (lzc & 0x3F); + int tmp_sign = df->sign; + + tmp_exp += 40; + tmp_walrus >>= 40; + + if (tmp_walrus == 0) + tmp_exp = -127; + else if (tmp_exp <= -127) + { + exception_flags |= flag_underflow | flag_inexact; + //printf("Subnormal: %lld. %d\n", tmp_walrus, tmp_exp); + if (1) + { + tmp_exp = -127; + tmp_walrus = 0; + } + else + { + tmp_walrus >>= -(tmp_exp + 126); + tmp_exp = -127; + } + } + else if (tmp_exp >= 128) + { + exception_flags |= flag_overflow; + //printf("Overflow!\n"); + + if (1) + tmp_exp -= 192; + else + { + tmp_exp = 128; + tmp_walrus = 0; + } + } + return (tmp_sign << 31) | ((tmp_exp + 127) << 23) | (tmp_walrus & 0x7FFFFF); +} + +uint32 V810_FP_Ops::mul(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + + if (fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return (~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + //printf("%08x %08x - %d %d %d - %d %d %d\n", a, b, a_exp, a_walrus, a_sign, b_exp, b_walrus, b_sign); + + res.exp = ins[0].exp + ins[1].exp - 23; + res.f = ins[0].f * ins[1].f; + res.sign = ins[0].sign ^ ins[1].sign; + + fpim_round(&res); + + return fpim_encode(&res); +} + +uint32 V810_FP_Ops::add(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + int64 ft[2]; + int64 tr; + int max_exp; + + if (fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return (~0U); + } + + if (a == b && !(a & 0x7FFFFFFF)) + { + return (a & 0x80000000); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + max_exp = std::max(ins[0].exp, ins[1].exp); + + //printf("%d:%08llx %d:%08llx\n", ins[0].exp, ins[0].f, ins[1].exp, ins[1].f); + + for (unsigned i = 0; i < 2; i++) + { + unsigned sd = (max_exp - ins[i].exp); + + ft[i] = ins[i].f << 24; + + if (sd >= 48) + { + if (ft[i] != 0) + ft[i] = 1; + } + else + { + int64 nft = ft[i] >> sd; + + if (ft[i] != (nft << sd)) + { + nft |= 1; + } + //{ + // puts("FPR"); + // } + + ft[i] = nft; + } + + if (ins[i].sign) + ft[i] = -ft[i]; + } + + //printf("SOON: %08llx %08llx\n", ft[0], ft[1]); + + tr = ft[0] + ft[1]; + if (tr < 0) + { + tr = -tr; + res.sign = true; + } + else + res.sign = false; + + res.f = tr; + res.exp = max_exp - 24; + + fpim_round(&res); + + return fpim_encode(&res); +} + +uint32 V810_FP_Ops::sub(uint32 a, uint32 b) +{ + return add(a, b ^ 0x80000000); +} + +uint32 V810_FP_Ops::div(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + uint64 mtmp; + + if (fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return (~0U); + } + + if (fp_is_zero(a) && fp_is_zero(b)) + { + exception_flags |= flag_invalid; + return (~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + res.sign = ins[0].sign ^ ins[1].sign; + + if (ins[1].f == 0) + { + //puts("Divide by zero!"); + exception_flags |= flag_divbyzero; + return ((res.sign << 31) | (255 << 23)); + } + else + { + res.exp = ins[0].exp - ins[1].exp - 2 - 1; // + 23 - 2; + res.f = ((ins[0].f << 24) / ins[1].f) << 2; + mtmp = ((ins[0].f << 24) % ins[1].f) << 1; + + //printf("%lld %lld\n", (ins[0].f << 23) % ins[1].f, ins[1].f); + + if (mtmp > ins[1].f) + res.f |= 3; + else if (mtmp == ins[1].f) + res.f |= 2; + else if (mtmp > 0) + res.f |= 1; + } + + fpim_round(&res); + + return fpim_encode(&res); +} + +int V810_FP_Ops::cmp(uint32 a, uint32 b) +{ + fpim ins[2]; + + if (fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return (~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + if (ins[0].exp > ins[1].exp) + return (ins[0].sign ? -1 : 1); + + if (ins[0].exp < ins[1].exp) + return (ins[1].sign ? 1 : -1); + + if (ins[0].f > ins[1].f) + return (ins[0].sign ? -1 : 1); + + if (ins[0].f < ins[1].f) + return (ins[1].sign ? 1 : -1); + + if ((ins[0].sign ^ ins[1].sign) && ins[0].f != 0) + return (ins[0].sign ? -1 : 1); + + return (0); +} + +uint32 V810_FP_Ops::itof(uint32 v) +{ + fpim res; + + res.sign = (bool)(v & 0x80000000); + res.exp = 23; + res.f = res.sign ? (0x80000000 - (v & 0x7FFFFFFF)) : (v & 0x7FFFFFFF); + + fpim_round(&res); + + return fpim_encode(&res); +} + +uint32 V810_FP_Ops::ftoi(uint32 v, bool truncate) +{ + fpim ins; + int sa; + int ret; + + if (fp_is_inf_nan_sub(v)) + { + exception_flags |= flag_reserved; + return (~0U); + } + + fpim_decode(&ins, v); + fpim_round_int(&ins, truncate); + + sa = ins.exp - 23; + + if (sa < 0) + { + if (sa <= -32) + ret = 0; + else + ret = ins.f >> -sa; + } + else + { + if (sa >= 8) + { + if (sa == 8 && ins.f == 0x800000 && ins.sign) + return (0x80000000); + else + { + ret = ~0U; + exception_flags |= flag_invalid; + } + } + else + { + ret = ins.f << sa; + } + } + //printf("%d\n", sa); + + if (ins.sign) + ret = -ret; + + return (ret); +} diff --git a/waterbox/vb/v810/v810_fp_ops.h b/waterbox/vb/v810/v810_fp_ops.h new file mode 100644 index 0000000000..20c607e7d2 --- /dev/null +++ b/waterbox/vb/v810/v810_fp_ops.h @@ -0,0 +1,74 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* v810_fp_ops.h: +** Copyright (C) 2014-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#pragma once + +class V810_FP_Ops +{ + public: + uint32 mul(uint32 a, uint32 b); + uint32 div(uint32 a, uint32 b); + uint32 add(uint32 a, uint32 b); + uint32 sub(uint32 a, uint32 b); + int cmp(uint32 a, uint32 b); + + uint32 itof(uint32 v); + uint32 ftoi(uint32 v, bool truncate); + + enum + { + flag_invalid = 0x0001, + flag_divbyzero = 0x0002, + flag_overflow = 0x0004, + flag_underflow = 0x0008, + flag_inexact = 0x0010, + flag_reserved = 0x0020 + }; + + inline uint32 get_flags(void) + { + return exception_flags; + } + + inline void clear_flags(void) + { + exception_flags = 0; + } + + private: + unsigned exception_flags; + + struct fpim + { + uint64 f; + int exp; + bool sign; + }; + + bool fp_is_zero(uint32 v); + bool fp_is_inf_nan_sub(uint32 v); + + unsigned clz64(uint64 v); + void fpim_decode(fpim *df, uint32 v); + void fpim_round(fpim *df); + void fpim_round_int(fpim *df, bool truncate = false); + uint32 fpim_encode(fpim *df); +}; diff --git a/waterbox/vb/v810/v810_oploop.inc b/waterbox/vb/v810/v810_oploop.inc new file mode 100644 index 0000000000..6b5015c744 --- /dev/null +++ b/waterbox/vb/v810/v810_oploop.inc @@ -0,0 +1,1130 @@ +/* V810 Emulator + * + * Copyright (C) 2006 David Tucker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + // Macro test taken from http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.dg/20020919-1.c?view=markup&pathrev=142696 + //#if defined (__powerpc__) || defined (__PPC__) || defined (__ppc__) || defined (__POWERPC__) || defined (PPC) || defined (_IBMR2) + // register v810_timestamp_t timestamp_rl asm("15") = v810_timestamp; + //#elif defined(__x86_64__) + // register v810_timestamp_t timestamp_rl asm("r11") = v810_timestamp; + //#else + register v810_timestamp_t timestamp_rl = v810_timestamp; + //#endif + + uint32 opcode; + uint32 tmp2; + int val = 0; + + + #define ADDCLOCK(__n) { timestamp += __n; } + + #define CHECK_HALTED(); { if(Halted && timestamp < next_event_ts) { timestamp = next_event_ts; } } + + while(Running) + { + #ifdef RB_DEBUGMODE + uint32 old_PC = RB_GETPC(); + #endif + uint32 tmpop; + + assert(timestamp_rl <= next_event_ts); + + if(!IPendingCache) + { + if(Halted) + { + timestamp_rl = next_event_ts; + } + else if(in_bstr) + { + tmpop = in_bstr_to; + opcode = tmpop >> 9; + goto op_BSTR; + } + } + + while(timestamp_rl < next_event_ts) + { + #ifdef RB_DEBUGMODE + old_PC = RB_GETPC(); + #endif + + P_REG[0] = 0; //Zero the Zero Reg!!! + + RB_CPUHOOK(RB_GETPC()); + + { + //printf("%08x\n", RB_GETPC()); + { + v810_timestamp_t timestamp = timestamp_rl; + + tmpop = RB_RDOP(0, 0); + + timestamp_rl = timestamp; + } + + opcode = (tmpop >> 9) | IPendingCache; + + //printf("%02x\n", opcode >> 1); +#if HAVE_COMPUTED_GOTO + #define CGBEGIN static const void *const op_goto_table[256] = { + #define CGE(l) &&l, + #define CGEND }; goto *op_goto_table[opcode]; +#else + /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more + expensive than computed goto which needs no masking nor bounds checking. + */ + #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opcode) { + #define CGE(l) case __COUNTER__ - CGESB: goto l; + #define CGEND } } +#endif + + CGBEGIN + CGE(op_MOV) CGE(op_MOV) CGE(op_ADD) CGE(op_ADD) CGE(op_SUB) CGE(op_SUB) CGE(op_CMP) CGE(op_CMP) + CGE(op_SHL) CGE(op_SHL) CGE(op_SHR) CGE(op_SHR) CGE(op_JMP) CGE(op_JMP) CGE(op_SAR) CGE(op_SAR) + CGE(op_MUL) CGE(op_MUL) CGE(op_DIV) CGE(op_DIV) CGE(op_MULU) CGE(op_MULU) CGE(op_DIVU) CGE(op_DIVU) + CGE(op_OR) CGE(op_OR) CGE(op_AND) CGE(op_AND) CGE(op_XOR) CGE(op_XOR) CGE(op_NOT) CGE(op_NOT) + CGE(op_MOV_I) CGE(op_MOV_I) CGE(op_ADD_I) CGE(op_ADD_I) CGE(op_SETF) CGE(op_SETF) CGE(op_CMP_I) CGE(op_CMP_I) + CGE(op_SHL_I) CGE(op_SHL_I) CGE(op_SHR_I) CGE(op_SHR_I) CGE(op_EI) CGE(op_EI) CGE(op_SAR_I) CGE(op_SAR_I) + CGE(op_TRAP) CGE(op_TRAP) CGE(op_RETI) CGE(op_RETI) CGE(op_HALT) CGE(op_HALT) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_LDSR) CGE(op_LDSR) CGE(op_STSR) CGE(op_STSR) CGE(op_DI) CGE(op_DI) CGE(op_BSTR) CGE(op_BSTR) + CGE(op_BV) CGE(op_BL) CGE(op_BE) CGE(op_BNH) CGE(op_BN) CGE(op_BR) CGE(op_BLT) CGE(op_BLE) + CGE(op_BNV) CGE(op_BNL) CGE(op_BNE) CGE(op_BH) CGE(op_BP) CGE(op_NOP) CGE(op_BGE) CGE(op_BGT) + CGE(op_MOVEA) CGE(op_MOVEA) CGE(op_ADDI) CGE(op_ADDI) CGE(op_JR) CGE(op_JR) CGE(op_JAL) CGE(op_JAL) + CGE(op_ORI) CGE(op_ORI) CGE(op_ANDI) CGE(op_ANDI) CGE(op_XORI) CGE(op_XORI) CGE(op_MOVHI) CGE(op_MOVHI) + CGE(op_LD_B) CGE(op_LD_B) CGE(op_LD_H) CGE(op_LD_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_LD_W) CGE(op_LD_W) + CGE(op_ST_B) CGE(op_ST_B) CGE(op_ST_H) CGE(op_ST_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_ST_W) CGE(op_ST_W) + CGE(op_IN_B) CGE(op_IN_B) CGE(op_IN_H) CGE(op_IN_H) CGE(op_CAXI) CGE(op_CAXI) CGE(op_IN_W) CGE(op_IN_W) + CGE(op_OUT_B) CGE(op_OUT_B) CGE(op_OUT_H) CGE(op_OUT_H) CGE(op_FPP) CGE(op_FPP) CGE(op_OUT_W) CGE(op_OUT_W) + + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INT_HANDLER) + CGEND + + // Bit string subopcodes + #define DO_AM_BSTR() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = (tmpop & 0x1F); \ + RB_INCPCBY2(); + + + #define DO_AM_FPP() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = (tmpop & 0x1F); \ + const uint32 arg3 = ((RB_RDOP(2) >> 10)&0x3F); \ + RB_INCPCBY4(); + + + #define DO_AM_UDEF() \ + RB_INCPCBY2(); + + #define DO_AM_I() \ + const uint32 arg1 = tmpop & 0x1F; \ + const uint32 arg2 = (tmpop >> 5) & 0x1F; \ + RB_INCPCBY2(); + + #define DO_AM_II() DO_AM_I(); + + + #define DO_AM_IV() \ + const uint32 arg1 = ((tmpop & 0x000003FF) << 16) | RB_RDOP(2); \ + + + #define DO_AM_V() \ + const uint32 arg3 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = tmpop & 0x1F; \ + const uint32 arg1 = RB_RDOP(2); \ + RB_INCPCBY4(); + + + #define DO_AM_VIa() \ + const uint32 arg1 = RB_RDOP(2); \ + const uint32 arg2 = tmpop & 0x1F; \ + const uint32 arg3 = (tmpop >> 5) & 0x1F; \ + RB_INCPCBY4(); \ + + + #define DO_AM_VIb() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = RB_RDOP(2); \ + const uint32 arg3 = (tmpop & 0x1F); \ + RB_INCPCBY4(); \ + + #define DO_AM_IX() \ + const uint32 arg1 = (tmpop & 0x1); \ + RB_INCPCBY2(); \ + + #define DO_AM_III() \ + const uint32 arg1 = tmpop & 0x1FE; + + #include "v810_do_am.h" + + #define BEGIN_OP(meowtmpop) { op_##meowtmpop: v810_timestamp_t timestamp = timestamp_rl; DO_##meowtmpop ##_AM(); + #define END_OP() timestamp_rl = timestamp; goto OpFinished; } + #define END_OP_SKIPLO() timestamp_rl = timestamp; goto OpFinishedSkipLO; } + + BEGIN_OP(MOV); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1]); + END_OP(); + + + BEGIN_OP(ADD); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + P_REG[arg1]; + + SetFlag(PSW_OV, ((P_REG[arg2]^(~P_REG[arg1]))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp < P_REG[arg2]); + + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SUB); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - P_REG[arg1]; + + SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(CMP); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - P_REG[arg1]; + + SetSZ(temp); + SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SHL); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + + // set CY before we destroy the regisrer info.... + SetFlag(PSW_CY, (val != 0) && ((P_REG[arg2] >> (32 - val))&0x01) ); + SetFlag(PSW_OV, FALSE); + SetPREG(arg2, P_REG[arg2] << val); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHR); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + // set CY before we destroy the regisrer info.... + SetFlag(PSW_CY, (val) && ((P_REG[arg2] >> (val-1))&0x01)); + SetFlag(PSW_OV, FALSE); + SetPREG(arg2, P_REG[arg2] >> val); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(JMP); + + (void)arg2; // arg2 is unused. + + ADDCLOCK(3); + RB_SETPC((P_REG[arg1] & 0xFFFFFFFE)); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(SAR); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + + SetFlag(PSW_CY, (val) && ((P_REG[arg2]>>(val-1))&0x01) ); + SetFlag(PSW_OV, FALSE); + + SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> val)); + + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(OR); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] | P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(AND); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] & P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(XOR); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] ^ P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(NOT); + ADDCLOCK(1); + SetPREG(arg2, ~P_REG[arg1]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(MOV_I); + ADDCLOCK(1); + SetPREG(arg2,sign_5(arg1)); + END_OP(); + + BEGIN_OP(ADD_I); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + sign_5(arg1); + + SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); + + SetPREG(arg2, (uint32)temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SETF); + ADDCLOCK(1); + + P_REG[arg2] = 0; + + switch (arg1 & 0x0F) + { + case COND_V: + if (TESTCOND_V) P_REG[arg2] = 1; + break; + case COND_C: + if (TESTCOND_C) P_REG[arg2] = 1; + break; + case COND_Z: + if (TESTCOND_Z) P_REG[arg2] = 1; + break; + case COND_NH: + if (TESTCOND_NH) P_REG[arg2] = 1; + break; + case COND_S: + if (TESTCOND_S) P_REG[arg2] = 1; + break; + case COND_T: + P_REG[arg2] = 1; + break; + case COND_LT: + if (TESTCOND_LT) P_REG[arg2] = 1; + break; + case COND_LE: + if (TESTCOND_LE) P_REG[arg2] = 1; + break; + case COND_NV: + if (TESTCOND_NV) P_REG[arg2] = 1; + break; + case COND_NC: + if (TESTCOND_NC) P_REG[arg2] = 1; + break; + case COND_NZ: + if (TESTCOND_NZ) P_REG[arg2] = 1; + break; + case COND_H: + if (TESTCOND_H) P_REG[arg2] = 1; + break; + case COND_NS: + if (TESTCOND_NS) P_REG[arg2] = 1; + break; + case COND_F: + //always false! do nothing more + break; + case COND_GE: + if (TESTCOND_GE) P_REG[arg2] = 1; + break; + case COND_GT: + if (TESTCOND_GT) P_REG[arg2] = 1; + break; + } + END_OP(); + + BEGIN_OP(CMP_I); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - sign_5(arg1); + + SetSZ(temp); + SetFlag(PSW_OV, ((P_REG[arg2]^(sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHR_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (arg1-1))&0x01) ); + // set CY before we destroy the regisrer info.... + SetPREG(arg2, P_REG[arg2] >> arg1); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHL_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (32 - arg1))&0x01) ); + // set CY before we destroy the regisrer info.... + + SetPREG(arg2, P_REG[arg2] << arg1); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SAR_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2]>>(arg1-1))&0x01) ); + + SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> arg1)); + + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(LDSR); // Loads a Sys Reg with the value in specified PR + ADDCLOCK(1); // ? + + SetSREG(timestamp, arg1 & 0x1F, P_REG[arg2 & 0x1F]); + END_OP(); + + BEGIN_OP(STSR); // Loads a PR with the value in specified Sys Reg + ADDCLOCK(1); // ? + P_REG[arg2 & 0x1F] = GetSREG(arg1 & 0x1F); + END_OP(); + + BEGIN_OP(EI); + (void)arg1; // arg1 is unused. + (void)arg2; // arg2 is unused. + + if(VBMode) + { + ADDCLOCK(1); + S_REG[PSW] = S_REG[PSW] &~ PSW_ID; + RecalcIPendingCache(); + } + else + { + ADDCLOCK(1); + RB_DECPCBY2(); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + BEGIN_OP(DI); + (void)arg1; // arg1 is unused. + (void)arg2; // arg2 is unused. + + if(VBMode) + { + ADDCLOCK(1); + S_REG[PSW] |= PSW_ID; + IPendingCache = 0; + } + else + { + ADDCLOCK(1); + RB_DECPCBY2(); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + + #define COND_BRANCH(cond) \ + if(cond) \ + { \ + ADDCLOCK(3); \ + RB_PCRELCHANGE(sign_9(arg1) & 0xFFFFFFFE); \ + if(RB_AccurateMode) \ + { \ + BRANCH_ALIGN_CHECK(PC); \ + } \ + RB_ADDBT(old_PC, RB_GETPC(), 0); \ + } \ + else \ + { \ + ADDCLOCK(1); \ + RB_INCPCBY2(); \ + } + + BEGIN_OP(BV); + COND_BRANCH(TESTCOND_V); + END_OP(); + + + BEGIN_OP(BL); + COND_BRANCH(TESTCOND_L); + END_OP(); + + BEGIN_OP(BE); + COND_BRANCH(TESTCOND_E); + END_OP(); + + BEGIN_OP(BNH); + COND_BRANCH(TESTCOND_NH); + END_OP(); + + BEGIN_OP(BN); + COND_BRANCH(TESTCOND_N); + END_OP(); + + BEGIN_OP(BR); + COND_BRANCH(TRUE); + END_OP(); + + BEGIN_OP(BLT); + COND_BRANCH(TESTCOND_LT); + END_OP(); + + BEGIN_OP(BLE); + COND_BRANCH(TESTCOND_LE); + END_OP(); + + BEGIN_OP(BNV); + COND_BRANCH(TESTCOND_NV); + END_OP(); + + BEGIN_OP(BNL); + COND_BRANCH(TESTCOND_NL); + END_OP(); + + BEGIN_OP(BNE); + COND_BRANCH(TESTCOND_NE); + END_OP(); + + BEGIN_OP(BH); + COND_BRANCH(TESTCOND_H); + END_OP(); + + BEGIN_OP(BP); + COND_BRANCH(TESTCOND_P); + END_OP(); + + BEGIN_OP(BGE); + COND_BRANCH(TESTCOND_GE); + END_OP(); + + BEGIN_OP(BGT); + COND_BRANCH(TESTCOND_GT); + END_OP(); + + BEGIN_OP(JR); + ADDCLOCK(3); + RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(JAL); + ADDCLOCK(3); + P_REG[31] = RB_GETPC() + 4; + RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(MOVEA); + ADDCLOCK(1); + SetPREG(arg3, P_REG[arg2] + sign_16(arg1)); + END_OP(); + + BEGIN_OP(ADDI); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + sign_16(arg1); + + SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_16(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); + + SetPREG(arg3, (uint32)temp); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(ORI); + ADDCLOCK(1); + SetPREG(arg3, arg1 | P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(ANDI); + ADDCLOCK(1); + SetPREG(arg3, (arg1 & P_REG[arg2])); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(XORI); + ADDCLOCK(1); + SetPREG(arg3, arg1 ^ P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(MOVHI); + ADDCLOCK(1); + SetPREG(arg3, (arg1 << 16) + P_REG[arg2]); + END_OP(); + + // LD.B + BEGIN_OP(LD_B); + ADDCLOCK(1); + tmp2 = (sign_16(arg1)+P_REG[arg2])&0xFFFFFFFF; + + SetPREG(arg3, sign_8(MemRead8(timestamp, tmp2))); + + //should be 3 clocks when executed alone, 2 when precedes another LD, or 1 + //when precedes an instruction with many clocks (I'm guessing FP, MUL, DIV, etc) + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + // LD.H + BEGIN_OP(LD_H); + ADDCLOCK(1); + tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE; + SetPREG(arg3, sign_16(MemRead16(timestamp, tmp2))); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + + // LD.W + BEGIN_OP(LD_W); + ADDCLOCK(1); + + tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC; + + if(MemReadBus32[tmp2 >> 24]) + { + SetPREG(arg3, MemRead32(timestamp, tmp2)); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + } + else + { + uint32 rv; + + rv = MemRead16(timestamp, tmp2); + rv |= MemRead16(timestamp, tmp2 | 2) << 16; + + SetPREG(arg3, rv); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(3); + } + else + { + ADDCLOCK(4); + } + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + // ST.B + BEGIN_OP(ST_B); + ADDCLOCK(1); + MemWrite8(timestamp, sign_16(arg2)+P_REG[arg3], P_REG[arg1] & 0xFF); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // ST.H + BEGIN_OP(ST_H); + ADDCLOCK(1); + + MemWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE, P_REG[arg1] & 0xFFFF); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // ST.W + BEGIN_OP(ST_W); + ADDCLOCK(1); + tmp2 = (sign_16(arg2)+P_REG[arg3]) & 0xFFFFFFFC; + + if(MemWriteBus32[tmp2 >> 24]) + { + MemWrite32(timestamp, tmp2, P_REG[arg1]); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + } + else + { + MemWrite16(timestamp, tmp2, P_REG[arg1] & 0xFFFF); + MemWrite16(timestamp, tmp2 | 2, P_REG[arg1] >> 16); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(3); + } + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // IN.B + BEGIN_OP(IN_B); + { + ADDCLOCK(3); + SetPREG(arg3, IORead8(timestamp, sign_16(arg1)+P_REG[arg2])); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // IN.H + BEGIN_OP(IN_H); + { + ADDCLOCK(3); + SetPREG(arg3, IORead16(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE)); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // IN.W + BEGIN_OP(IN_W); + if(IORead32) + { + ADDCLOCK(3); + SetPREG(arg3, IORead32(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC)); + } + else + { + uint32 eff_addr = (sign_16(arg1) + P_REG[arg2]) & 0xFFFFFFFC; + uint32 rv; + + ADDCLOCK(5); + + rv = IORead16(timestamp, eff_addr); + rv |= IORead16(timestamp, eff_addr | 2) << 16; + + SetPREG(arg3, rv); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // OUT.B + BEGIN_OP(OUT_B); + ADDCLOCK(1); + IOWrite8(timestamp, sign_16(arg2)+P_REG[arg3],P_REG[arg1]&0xFF); + + if(lastop == LASTOP_OUT) + { + ADDCLOCK(1); + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + + // OUT.H + BEGIN_OP(OUT_H); + ADDCLOCK(1); + IOWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE,P_REG[arg1]&0xFFFF); + + if(lastop == LASTOP_OUT) + { + ADDCLOCK(1); + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + + // OUT.W + BEGIN_OP(OUT_W); + ADDCLOCK(1); + + if(IOWrite32) + IOWrite32(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC,P_REG[arg1]); + else + { + uint32 eff_addr = (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC; + IOWrite16(timestamp, eff_addr, P_REG[arg1] & 0xFFFF); + IOWrite16(timestamp, eff_addr | 2, P_REG[arg1] >> 16); + } + + if(lastop == LASTOP_OUT) + { + if(IOWrite32) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(3); + } + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + BEGIN_OP(NOP); + (void)arg1; // arg1 is unused. + + ADDCLOCK(1); + RB_INCPCBY2(); + END_OP(); + + BEGIN_OP(RETI); + (void)arg1; // arg1 is unused. + + ADDCLOCK(10); + + //Return from Trap/Interupt + if(S_REG[PSW] & PSW_NP) { // Read the FE Reg + RB_SETPC(S_REG[FEPC] & 0xFFFFFFFE); + S_REG[PSW] = S_REG[FEPSW]; + } else { //Read the EI Reg Interupt + RB_SETPC(S_REG[EIPC] & 0xFFFFFFFE); + S_REG[PSW] = S_REG[EIPSW]; + } + RecalcIPendingCache(); + + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(MUL); + ADDCLOCK(13); + + uint64 temp = (int64)(int32)P_REG[arg1] * (int32)P_REG[arg2]; + + SetPREG(30, (uint32)(temp >> 32)); + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + SetFlag(PSW_OV, temp != (uint64)(int64)(int32)(uint32)temp); + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(MULU); + ADDCLOCK(13); + uint64 temp = (uint64)P_REG[arg1] * (uint64)P_REG[arg2]; + + SetPREG(30, (uint32)(temp >> 32)); + SetPREG(arg2, (uint32)temp); + + SetSZ(P_REG[arg2]); + SetFlag(PSW_OV, temp != (uint32)temp); + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(DIVU); + ADDCLOCK(36); + if(P_REG[arg1] == 0) // Divide by zero! + { + RB_DECPCBY2(); + Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); + CHECK_HALTED(); + } + else + { + // Careful here, since arg2 can be == 30 + uint32 quotient = (uint32)P_REG[arg2] / (uint32)P_REG[arg1]; + uint32 remainder = (uint32)P_REG[arg2] % (uint32)P_REG[arg1]; + + SetPREG(30, remainder); + SetPREG(arg2, quotient); + + SetFlag(PSW_OV, FALSE); + SetSZ(quotient); + } + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(DIV); + //if(P_REG[arg1] & P_REG[arg2] & 0x80000000) + //{ + // printf("Div: %08x %08x\n", P_REG[arg1], P_REG[arg2]); + //} + + ADDCLOCK(38); + if((uint32)P_REG[arg1] == 0) // Divide by zero! + { + RB_DECPCBY2(); + Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); + CHECK_HALTED(); + } + else + { + if((P_REG[arg2]==0x80000000)&&(P_REG[arg1]==0xFFFFFFFF)) + { + SetFlag(PSW_OV, TRUE); + P_REG[30]=0; + SetPREG(arg2, 0x80000000); + SetSZ(P_REG[arg2]); + } + else + { + // Careful here, since arg2 can be == 30 + uint32 quotient = (int32)P_REG[arg2] / (int32)P_REG[arg1]; + uint32 remainder = (int32)P_REG[arg2] % (int32)P_REG[arg1]; + + SetPREG(30, remainder); + SetPREG(arg2, quotient); + + SetFlag(PSW_OV, FALSE); + SetSZ(quotient); + } + } + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(FPP); + ADDCLOCK(1); + fpu_subop(timestamp, arg3, arg1, arg2); + lastop = -1; + CHECK_HALTED(); + END_OP_SKIPLO(); + + BEGIN_OP(BSTR); + if(!in_bstr) + { + ADDCLOCK(1); + } + + if(bstr_subop(timestamp, arg2, arg1)) + { + RB_DECPCBY2(); + in_bstr = TRUE; + in_bstr_to = tmpop; + } + else + { + in_bstr = FALSE; + have_src_cache = have_dst_cache = FALSE; + } + END_OP(); + + BEGIN_OP(HALT); + (void)arg1; // arg1 is unused. + + ADDCLOCK(1); + Halted = HALT_HALT; + //printf("Untested opcode: HALT\n"); + END_OP(); + + BEGIN_OP(TRAP); + (void)arg2; // arg2 is unused. + + ADDCLOCK(15); + + Exception(TRAP_HANDLER_BASE + (arg1 & 0x10), ECODE_TRAP_BASE + (arg1 & 0x1F)); + CHECK_HALTED(); + END_OP(); + + BEGIN_OP(CAXI); + //printf("Untested opcode: caxi\n"); + + // Lock bus(N/A) + + ADDCLOCK(26); + + { + uint32 addr, tmp, compare_temp; + uint32 to_write; + + addr = sign_16(arg1) + P_REG[arg2]; + addr &= ~3; + + if(MemReadBus32[addr >> 24]) + tmp = MemRead32(timestamp, addr); + else + { + tmp = MemRead16(timestamp, addr); + tmp |= MemRead16(timestamp, addr | 2) << 16; + } + + compare_temp = P_REG[arg3] - tmp; + + SetSZ(compare_temp); + SetFlag(PSW_OV, ((P_REG[arg3]^tmp)&(P_REG[arg3]^compare_temp))&0x80000000); + SetFlag(PSW_CY, compare_temp > P_REG[arg3]); + + if(!compare_temp) // If they're equal... + to_write = P_REG[30]; + else + to_write = tmp; + + if(MemWriteBus32[addr >> 24]) + MemWrite32(timestamp, addr, to_write); + else + { + MemWrite16(timestamp, addr, to_write & 0xFFFF); + MemWrite16(timestamp, addr | 2, to_write >> 16); + } + P_REG[arg3] = tmp; + } + + // Unlock bus(N/A) + + END_OP(); + + + + op_INT_HANDLER: + { + int iNum = ilevel; + + S_REG[EIPC] = GetPC(); + S_REG[EIPSW] = S_REG[PSW]; + + SetPC(0xFFFFFE00 | (iNum << 4)); + + RB_ADDBT(old_PC, RB_GETPC(), 0xFE00 | (iNum << 4)); + + S_REG[ECR] = 0xFE00 | (iNum << 4); + + S_REG[PSW] |= PSW_EP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + // Now, set need to set the interrupt enable level to he level that is being processed + 1, + // saturating at 15. + iNum++; + + if(iNum > 0x0F) + iNum = 0x0F; + + S_REG[PSW] &= ~PSW_IA; + S_REG[PSW] |= iNum << 16; + + // Accepting an interrupt takes us out of normal HALT status, of course! + Halted = HALT_NONE; + + // Invalidate our bitstring state(forces the instruction to be re-read, and the r/w buffers reloaded). + in_bstr = FALSE; + have_src_cache = FALSE; + have_dst_cache = FALSE; + + IPendingCache = 0; + + goto OpFinished; + } + + + BEGIN_OP(INVALID); + RB_DECPCBY2(); + if(!RB_AccurateMode) + { + RB_SETPC(RB_GETPC()); + if((uint32)(RB_RDOP(0, 0) >> 9) != opcode) + { + //printf("Trampoline: %08x %02x\n", RB_GETPC(), opcode >> 1); + } + else + { + ADDCLOCK(1); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + } + else + { + ADDCLOCK(1); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + } + + OpFinished: ; + lastop = opcode; + OpFinishedSkipLO: ; + } // end while(timestamp_rl < next_event_ts) + next_event_ts = event_handler(timestamp_rl); + //printf("Next: %d, Cur: %d\n", next_event_ts, timestamp); + } + +v810_timestamp = timestamp_rl; diff --git a/waterbox/vb/v810/v810_opt.h b/waterbox/vb/v810/v810_opt.h new file mode 100644 index 0000000000..585c50b269 --- /dev/null +++ b/waterbox/vb/v810/v810_opt.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////// +// File: v810_opt.h +// +// Description: Defines used in v810_dis.cpp +// + +#ifndef V810_OPT_H_ +#define V810_OPT_H_ + +#define sign_26(num) ((uint32)sign_x_to_s32(26, num)) +#define sign_16(num) ((uint32)(int16)(num)) +#define sign_14(num) ((uint32)sign_x_to_s32(14, num)) +#define sign_12(num) ((uint32)sign_x_to_s32(12, num)) +#define sign_9(num) ((uint32)sign_x_to_s32(9, num)) +#define sign_8(_value) ((uint32)(int8)(_value)) +#define sign_5(num) ((uint32)sign_x_to_s32(5, num)) + +/////////////////////////////////////////////////////////////////// +// Define Modes +#define AM_I 0x01 +#define AM_II 0x02 +#define AM_III 0x03 +#define AM_IV 0x04 +#define AM_V 0x05 +#define AM_VIa 0x06 // Mode6 form1 +#define AM_VIb 0x0A // Mode6 form2 +#define AM_VII 0x07 +#define AM_VIII 0x08 +#define AM_IX 0x09 +#define AM_BSTR 0x0B // Bit String Instructions +#define AM_FPP 0x0C // Floating Point Instructions +#define AM_UDEF 0x0D // Unknown/Undefined Instructions + +/////////////////////////////////////////////////////////////////// +// Table of Instructions Address Modes + +static const int addr_mode[80] = { + AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, + AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, + AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, + AM_II, AM_IX, AM_IX, AM_UDEF, AM_II, AM_II, AM_II, AM_BSTR, + AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, + AM_V, AM_V, AM_IV, AM_IV, AM_V, AM_V, AM_V, AM_V, + AM_VIa, AM_VIa, AM_UDEF, AM_VIa, AM_VIb, AM_VIb, AM_UDEF, AM_VIb, + AM_VIa, AM_VIa, AM_VIa, AM_VIa, AM_VIb, AM_VIb, AM_FPP, AM_VIb, + AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, + AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III +}; +// All instructions greater than 0x50 are undefined (this should not be posible of cource) + + +/////////////////////////////////////////////////////////////////// +// Opcodes for V810 Instruction set +#define MOV 0x00 +#define ADD 0x01 +#define SUB 0x02 +#define CMP 0x03 +#define SHL 0x04 +#define SHR 0x05 +#define JMP 0x06 +#define SAR 0x07 +#define MUL 0x08 +#define DIV 0x09 +#define MULU 0x0A +#define DIVU 0x0B +#define OR 0x0C +#define AND 0x0D +#define XOR 0x0E +#define NOT 0x0F +#define MOV_I 0x10 +#define ADD_I 0x11 +#define SETF 0x12 +#define CMP_I 0x13 +#define SHL_I 0x14 +#define SHR_I 0x15 +#define EI 0x16 +#define SAR_I 0x17 +#define TRAP 0x18 +#define RETI 0x19 +#define HALT 0x1A + //0x1B +#define LDSR 0x1C +#define STSR 0x1D +#define DI 0x1E +#define BSTR 0x1F //Special Bit String Inst + //0x20 - 0x27 // Lost to Branch Instructions +#define MOVEA 0x28 +#define ADDI 0x29 +#define JR 0x2A +#define JAL 0x2B +#define ORI 0x2C +#define ANDI 0x2D +#define XORI 0x2E +#define MOVHI 0x2F +#define LD_B 0x30 +#define LD_H 0x31 + //0x32 +#define LD_W 0x33 +#define ST_B 0x34 +#define ST_H 0x35 + //0x36 +#define ST_W 0x37 +#define IN_B 0x38 +#define IN_H 0x39 +#define CAXI 0x3A +#define IN_W 0x3B +#define OUT_B 0x3C +#define OUT_H 0x3D +#define FPP 0x3E //Special Float Inst +#define OUT_W 0x3F + + +// Branch Instructions ( Extended opcode only for Branch command) +// Common instrcutions commented out + +#define BV 0x40 +#define BL 0x41 +#define BE 0x42 +#define BNH 0x43 +#define BN 0x44 +#define BR 0x45 +#define BLT 0x46 +#define BLE 0x47 +#define BNV 0x48 +#define BNL 0x49 +#define BNE 0x4A +#define BH 0x4B +#define BP 0x4C +#define NOP 0x4D +#define BGE 0x4E +#define BGT 0x4F + +//#define BC 0x41 +//#define BZ 0x42 +//#define BNC 0x49 +//#define BNZ 0x4A + +// Bit String Subopcodes +#define SCH0BSU 0x00 +#define SCH0BSD 0x01 +#define SCH1BSU 0x02 +#define SCH1BSD 0x03 + +#define ORBSU 0x08 +#define ANDBSU 0x09 +#define XORBSU 0x0A +#define MOVBSU 0x0B +#define ORNBSU 0x0C +#define ANDNBSU 0x0D +#define XORNBSU 0x0E +#define NOTBSU 0x0F + + +// Floating Point Subopcodes +#define CMPF_S 0x00 + +#define CVT_WS 0x02 +#define CVT_SW 0x03 +#define ADDF_S 0x04 +#define SUBF_S 0x05 +#define MULF_S 0x06 +#define DIVF_S 0x07 +#define XB 0x08 +#define XH 0x09 +#define REV 0x0A +#define TRNC_SW 0x0B +#define MPYHW 0x0C + +#endif //DEFINE_H + diff --git a/waterbox/vb/vb.cpp b/waterbox/vb/vb.cpp new file mode 100644 index 0000000000..b9940aa171 --- /dev/null +++ b/waterbox/vb/vb.cpp @@ -0,0 +1,780 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vb.cpp: +** Copyright (C) 2010-2017 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" +#include +#define EXPORT extern "C" ECL_EXPORT + +namespace MDFN_IEN_VB +{ +enum +{ + ANAGLYPH_PRESET_DISABLED = 0, + ANAGLYPH_PRESET_RED_BLUE, + ANAGLYPH_PRESET_RED_CYAN, + ANAGLYPH_PRESET_RED_ELECTRICCYAN, + ANAGLYPH_PRESET_RED_GREEN, + ANAGLYPH_PRESET_GREEN_MAGENTA, + ANAGLYPH_PRESET_YELLOW_BLUE, +}; + +static const uint32 AnaglyphPreset_Colors[][2] = + { + {0, 0}, + {0xFF0000, 0x0000FF}, + {0xFF0000, 0x00B7EB}, + {0xFF0000, 0x00FFFF}, + {0xFF0000, 0x00FF00}, + {0x00FF00, 0xFF00FF}, + {0xFFFF00, 0x0000FF}, +}; + +int32 VB_InDebugPeek; + +static uint32 VB3DMode; + +static uint8 *WRAM = NULL; + +static uint8 *GPRAM = NULL; +static const uint32 GPRAM_Mask = 0xFFFF; + +static uint8 *GPROM = NULL; +static uint32 GPROM_Mask; + +V810 *VB_V810 = NULL; + +VSU *VB_VSU = NULL; +static uint32 VSU_CycleFix; + +static uint8 WCR; + +static int32 next_vip_ts, next_timer_ts, next_input_ts; + +static uint32 IRQ_Asserted; + +static INLINE void RecalcIntLevel(void) +{ + int ilevel = -1; + + for (int i = 4; i >= 0; i--) + { + if (IRQ_Asserted & (1 << i)) + { + ilevel = i; + break; + } + } + + VB_V810->SetInt(ilevel); +} + +void VBIRQ_Assert(int source, bool assert) +{ + assert(source >= 0 && source <= 4); + + IRQ_Asserted &= ~(1 << source); + + if (assert) + IRQ_Asserted |= 1 << source; + + RecalcIntLevel(); +} + +static MDFN_FASTCALL uint8 HWCTRL_Read(v810_timestamp_t ×tamp, uint32 A) +{ + uint8 ret = 0; + + if (A & 0x3) + { + //puts("HWCtrl Bogus Read?"); + return (ret); + } + + switch (A & 0xFF) + { + default: //printf("Unknown HWCTRL Read: %08x\n", A); + break; + + case 0x18: + case 0x1C: + case 0x20: + ret = TIMER_Read(timestamp, A); + break; + + case 0x24: + ret = WCR | 0xFC; + break; + + case 0x10: + case 0x14: + case 0x28: + ret = VBINPUT_Read(timestamp, A); + break; + } + + return (ret); +} + +static MDFN_FASTCALL void HWCTRL_Write(v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + if (A & 0x3) + { + puts("HWCtrl Bogus Write?"); + return; + } + + switch (A & 0xFF) + { + default: //printf("Unknown HWCTRL Write: %08x %02x\n", A, V); + break; + + case 0x18: + case 0x1C: + case 0x20: + TIMER_Write(timestamp, A, V); + break; + + case 0x24: + WCR = V & 0x3; + break; + + case 0x10: + case 0x14: + case 0x28: + VBINPUT_Write(timestamp, A, V); + break; + } +} + +uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t ×tamp, uint32 A) +{ + uint8 ret = 0; + A &= (1 << 27) - 1; + + //if((A >> 24) <= 2) + // printf("Read8: %d %08x\n", timestamp, A); + + switch (A >> 24) + { + case 0: + ret = VIP_Read8(timestamp, A); + break; + + case 1: + break; + + case 2: + ret = HWCTRL_Read(timestamp, A); + break; + + case 3: + break; + case 4: + break; + + case 5: + ret = WRAM[A & 0xFFFF]; + break; + + case 6: + if (GPRAM) + ret = GPRAM[A & GPRAM_Mask]; + break; + + case 7: + ret = GPROM[A & GPROM_Mask]; + break; + } + return (ret); +} + +uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t ×tamp, uint32 A) +{ + uint16 ret = 0; + + A &= (1 << 27) - 1; + + //if((A >> 24) <= 2) + // printf("Read16: %d %08x\n", timestamp, A); + + switch (A >> 24) + { + case 0: + ret = VIP_Read16(timestamp, A); + break; + + case 1: + break; + + case 2: + ret = HWCTRL_Read(timestamp, A); + break; + + case 3: + break; + + case 4: + break; + + case 5: + ret = MDFN_de16lsb(&WRAM[A & 0xFFFF]); + break; + + case 6: + if (GPRAM) + ret = MDFN_de16lsb(&GPRAM[A & GPRAM_Mask]); + break; + + case 7: + ret = MDFN_de16lsb(&GPROM[A & GPROM_Mask]); + break; + } + return ret; +} + +void MDFN_FASTCALL MemWrite8(v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + A &= (1 << 27) - 1; + + //if((A >> 24) <= 2) + // printf("Write8: %d %08x %02x\n", timestamp, A, V); + + switch (A >> 24) + { + case 0: + VIP_Write8(timestamp, A, V); + break; + + case 1: + VB_VSU->Write((timestamp + VSU_CycleFix) >> 2, A, V); + break; + + case 2: + HWCTRL_Write(timestamp, A, V); + break; + + case 3: + break; + + case 4: + break; + + case 5: + WRAM[A & 0xFFFF] = V; + break; + + case 6: + if (GPRAM) + GPRAM[A & GPRAM_Mask] = V; + break; + + case 7: // ROM, no writing allowed! + break; + } +} + +void MDFN_FASTCALL MemWrite16(v810_timestamp_t ×tamp, uint32 A, uint16 V) +{ + A &= (1 << 27) - 1; + + //if((A >> 24) <= 2) + // printf("Write16: %d %08x %04x\n", timestamp, A, V); + + switch (A >> 24) + { + case 0: + VIP_Write16(timestamp, A, V); + break; + + case 1: + VB_VSU->Write((timestamp + VSU_CycleFix) >> 2, A, V); + break; + + case 2: + HWCTRL_Write(timestamp, A, V); + break; + + case 3: + break; + + case 4: + break; + + case 5: + MDFN_en16lsb(&WRAM[A & 0xFFFF], V); + break; + + case 6: + if (GPRAM) + MDFN_en16lsb(&GPRAM[A & GPRAM_Mask], V); + break; + + case 7: // ROM, no writing allowed! + break; + } +} + +static void FixNonEvents(void) +{ + if (next_vip_ts & 0x40000000) + next_vip_ts = VB_EVENT_NONONO; + + if (next_timer_ts & 0x40000000) + next_timer_ts = VB_EVENT_NONONO; + + if (next_input_ts & 0x40000000) + next_input_ts = VB_EVENT_NONONO; +} + +static void EventReset(void) +{ + next_vip_ts = VB_EVENT_NONONO; + next_timer_ts = VB_EVENT_NONONO; + next_input_ts = VB_EVENT_NONONO; +} + +static INLINE int32 CalcNextTS(void) +{ + int32 next_timestamp = next_vip_ts; + + if (next_timestamp > next_timer_ts) + next_timestamp = next_timer_ts; + + if (next_timestamp > next_input_ts) + next_timestamp = next_input_ts; + + return (next_timestamp); +} + +static void RebaseTS(const v810_timestamp_t timestamp) +{ + //printf("Rebase: %08x %08x %08x\n", timestamp, next_vip_ts, next_timer_ts); + + assert(next_vip_ts > timestamp); + assert(next_timer_ts > timestamp); + assert(next_input_ts > timestamp); + + next_vip_ts -= timestamp; + next_timer_ts -= timestamp; + next_input_ts -= timestamp; +} + +void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp) +{ + //assert(next_timestamp > VB_V810->v810_timestamp); + + if (type == VB_EVENT_VIP) + next_vip_ts = next_timestamp; + else if (type == VB_EVENT_TIMER) + next_timer_ts = next_timestamp; + else if (type == VB_EVENT_INPUT) + next_input_ts = next_timestamp; + + if (next_timestamp < VB_V810->GetEventNT()) + VB_V810->SetEventNT(next_timestamp); +} + +static int32 MDFN_FASTCALL EventHandler(const v810_timestamp_t timestamp) +{ + if (timestamp >= next_vip_ts) + next_vip_ts = VIP_Update(timestamp); + + if (timestamp >= next_timer_ts) + next_timer_ts = TIMER_Update(timestamp); + + if (timestamp >= next_input_ts) + next_input_ts = VBINPUT_Update(timestamp); + + return (CalcNextTS()); +} + +// Called externally from debug.cpp in some cases. +void ForceEventUpdates(const v810_timestamp_t timestamp) +{ + next_vip_ts = VIP_Update(timestamp); + next_timer_ts = TIMER_Update(timestamp); + next_input_ts = VBINPUT_Update(timestamp); + + VB_V810->SetEventNT(CalcNextTS()); + //printf("FEU: %d %d %d\n", next_vip_ts, next_timer_ts, next_input_ts); +} + +static void VB_Power(void) +{ + memset(WRAM, 0, 65536); + + VIP_Power(); + VB_VSU->Power(); + TIMER_Power(); + VBINPUT_Power(); + + EventReset(); + IRQ_Asserted = 0; + RecalcIntLevel(); + VB_V810->Reset(); + + VSU_CycleFix = 0; + WCR = 0; + + ForceEventUpdates(0); //VB_V810->v810_timestamp); +} + +/*struct VB_HeaderInfo +{ + char game_title[256]; + uint32 game_code; + uint16 manf_code; + uint8 version; +};*/ + +/*static void ReadHeader(const uint8 *const rom_data, const uint64 rom_size, VB_HeaderInfo *hi) +{ + iconv_t sjis_ict = iconv_open("UTF-8", "shift_jis"); + + if (sjis_ict != (iconv_t)-1) + { + char *in_ptr, *out_ptr; + size_t ibl, obl; + + ibl = 20; + obl = sizeof(hi->game_title) - 1; + + in_ptr = (char *)rom_data + (0xFFFFFDE0 & (rom_size - 1)); + out_ptr = hi->game_title; + + iconv(sjis_ict, (ICONV_CONST char **)&in_ptr, &ibl, &out_ptr, &obl); + iconv_close(sjis_ict); + + *out_ptr = 0; + + MDFN_zapctrlchars(hi->game_title); + MDFN_trim(hi->game_title); + } + else + hi->game_title[0] = 0; + + hi->game_code = MDFN_de32lsb(rom_data + (0xFFFFFDFB & (rom_size - 1))); + hi->manf_code = MDFN_de16lsb(rom_data + (0xFFFFFDF9 & (rom_size - 1))); + hi->version = rom_data[0xFFFFFDFF & (rom_size - 1)]; +}*/ + +void VB_ExitLoop(void) +{ + VB_V810->Exit(); +} + + +/*MDFNGI EmulatedVB = + { + + PortInfo, + Load, + TestMagic, + NULL, + NULL, + CloseGame, + + SetLayerEnableMask, + NULL, // Layer names, null-delimited + + NULL, + NULL, + + VIP_CPInfo, + 1 << 0, + + CheatInfo_Empty, + + false, + StateAction, + Emulate, + NULL, + VBINPUT_SetInput, + NULL, + DoSimpleCommand, + NULL, + VBSettings, + MDFN_MASTERCLOCK_FIXED(VB_MASTER_CLOCK), + 0, + false, // Multires possible? + + 0, // lcm_width + 0, // lcm_height + NULL, // Dummy + + 384, // Nominal width + 224, // Nominal height + + 384, // Framebuffer width + 256, // Framebuffer height + + 2, // Number of output sound channels +};*/ +} + +using namespace MDFN_IEN_VB; + +EXPORT int Load(const uint8 *rom, int length) +{ + const uint64 rom_size = length; + V810_Emu_Mode cpu_mode = V810_EMU_MODE_ACCURATE; + + VB_InDebugPeek = 0; + + if (rom_size != round_up_pow2(rom_size)) + { + return 0; + // throw MDFN_Error(0, _("VB ROM image size is not a power of 2.")); + } + + if (rom_size < 256) + { + return 0; + //throw MDFN_Error(0, _("VB ROM image size is too small.")); + } + + if (rom_size > (1 << 24)) + { + return 0; + //throw MDFN_Error(0, _("VB ROM image size is too large.")); + } + + VB_V810 = new V810(); + VB_V810->Init(cpu_mode, true); + + VB_V810->SetMemReadHandlers(MemRead8, MemRead16, NULL); + VB_V810->SetMemWriteHandlers(MemWrite8, MemWrite16, NULL); + + VB_V810->SetIOReadHandlers(MemRead8, MemRead16, NULL); + VB_V810->SetIOWriteHandlers(MemWrite8, MemWrite16, NULL); + + for (int i = 0; i < 256; i++) + { + VB_V810->SetMemReadBus32(i, false); + VB_V810->SetMemWriteBus32(i, false); + } + + std::vector Map_Addresses; + + for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27)) + { + for (uint64 sub_A = 5 << 24; sub_A < (6 << 24); sub_A += 65536) + { + Map_Addresses.push_back(A + sub_A); + } + } + + WRAM = VB_V810->SetFastMap(&Map_Addresses[0], 65536, Map_Addresses.size(), "WRAM"); + Map_Addresses.clear(); + + // Round up the ROM size to 65536(we mirror it a little later) + GPROM_Mask = (rom_size < 65536) ? (65536 - 1) : (rom_size - 1); + + for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27)) + { + for (uint64 sub_A = 7 << 24; sub_A < (8 << 24); sub_A += GPROM_Mask + 1) + { + Map_Addresses.push_back(A + sub_A); + //printf("%08x\n", (uint32)(A + sub_A)); + } + } + + GPROM = VB_V810->SetFastMap(&Map_Addresses[0], GPROM_Mask + 1, Map_Addresses.size(), "Cart ROM"); + Map_Addresses.clear(); + + memcpy(GPROM, rom, rom_size); + + // Mirror ROM images < 64KiB to 64KiB + for (uint64 i = rom_size; i < 65536; i += rom_size) + { + memcpy(GPROM + i, GPROM, rom_size); + } + + /*VB_HeaderInfo hinfo; + + ReadHeader(GPROM, rom_size, &hinfo); + + MDFN_printf(_("Title: %s\n"), hinfo.game_title); + MDFN_printf(_("Game ID Code: %u\n"), hinfo.game_code); + MDFN_printf(_("Manufacturer Code: %d\n"), hinfo.manf_code); + MDFN_printf(_("Version: %u\n"), hinfo.version); + + MDFN_printf(_("ROM: %uKiB\n"), (unsigned)(rom_size / 1024)); + MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());*/ + + /*MDFN_printf("\n"); + + MDFN_printf(_("V810 Emulation Mode: %s\n"), (cpu_mode == V810_EMU_MODE_ACCURATE) ? _("Accurate") : _("Fast"));*/ + + for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27)) + { + for (uint64 sub_A = 6 << 24; sub_A < (7 << 24); sub_A += GPRAM_Mask + 1) + { + //printf("GPRAM: %08x\n", A + sub_A); + Map_Addresses.push_back(A + sub_A); + } + } + + GPRAM = VB_V810->SetFastMap(&Map_Addresses[0], GPRAM_Mask + 1, Map_Addresses.size(), "Cart RAM"); + Map_Addresses.clear(); + + memset(GPRAM, 0, GPRAM_Mask + 1); + + VIP_Init(); + VB_VSU = new VSU(); + VBINPUT_Init(); + + VB3DMode = VB3DMODE_ANAGLYPH; + uint32 prescale = 2; + uint32 sbs_separation = 0; + bool reverse = false; + + VIP_Set3DMode(VB3DMode, reverse, prescale, sbs_separation); + + VIP_SetParallaxDisable(false); + { + auto presetColor = ANAGLYPH_PRESET_RED_BLUE; + + uint32 lcolor, rcolor; + + if (presetColor != ANAGLYPH_PRESET_DISABLED) + { + lcolor = AnaglyphPreset_Colors[presetColor][0]; + rcolor = AnaglyphPreset_Colors[presetColor][1]; + } + VIP_SetAnaglyphColors(lcolor, rcolor); + VIP_SetDefaultColor(0xf0f0f0); + } + + VBINPUT_SetInstantReadHack(true); + + VIP_SetLEDOnScale(1.75); + + //MDFNGameInfo->fps = (int64)20000000 * 65536 * 256 / (259 * 384 * 4); + + VB_Power(); + + /*MDFNGameInfo->nominal_width = 384; + MDFNGameInfo->nominal_height = 224; + MDFNGameInfo->fb_width = 384; + MDFNGameInfo->fb_height = 224;*/ + + /*switch (VB3DMode) + { + default: + break; + + case VB3DMODE_VLI: + MDFNGameInfo->nominal_width = 768 * prescale; + MDFNGameInfo->nominal_height = 224; + MDFNGameInfo->fb_width = 768 * prescale; + MDFNGameInfo->fb_height = 224; + break; + + case VB3DMODE_HLI: + MDFNGameInfo->nominal_width = 384; + MDFNGameInfo->nominal_height = 448 * prescale; + MDFNGameInfo->fb_width = 384; + MDFNGameInfo->fb_height = 448 * prescale; + break; + + case VB3DMODE_CSCOPE: + MDFNGameInfo->nominal_width = 512; + MDFNGameInfo->nominal_height = 384; + MDFNGameInfo->fb_width = 512; + MDFNGameInfo->fb_height = 384; + break; + + case VB3DMODE_SIDEBYSIDE: + MDFNGameInfo->nominal_width = 384 * 2 + sbs_separation; + MDFNGameInfo->nominal_height = 224; + MDFNGameInfo->fb_width = 384 * 2 + sbs_separation; + MDFNGameInfo->fb_height = 224; + break; + } + MDFNGameInfo->lcm_width = MDFNGameInfo->fb_width; + MDFNGameInfo->lcm_height = MDFNGameInfo->fb_height;*/ + + VB_VSU->SetSoundRate(44100); + + return 1; +} + +EXPORT void GetMemoryArea(int which, void **ptr, int *size) +{ + switch (which) + { + case 0: + *ptr = WRAM; + *size = 65536; + break; + case 1: + *ptr = GPRAM; + *size = GPRAM_Mask + 1; + break; + case 2: + *ptr = GPROM; + *size = GPROM_Mask + 1; + break; + default: + *ptr = nullptr; + *size = 0; + break; + } +} + +EXPORT void Emulate(EmulateSpecStruct *espec) +{ + v810_timestamp_t v810_timestamp; + + VBINPUT_Frame(&espec->Buttons); + + VIP_StartFrame(espec); + + v810_timestamp = VB_V810->Run(EventHandler); + + FixNonEvents(); + ForceEventUpdates(v810_timestamp); + + espec->SoundBufSize = VB_VSU->EndFrame((v810_timestamp + VSU_CycleFix) >> 2, espec->SoundBuf, espec->SoundBufMaxSize); + + VSU_CycleFix = (v810_timestamp + VSU_CycleFix) & 3; + + espec->MasterCycles = v810_timestamp; + + TIMER_ResetTS(); + VBINPUT_ResetTS(); + VIP_ResetTS(); + + RebaseTS(v810_timestamp); + + VB_V810->ResetTS(0); +} + +EXPORT void HardReset() +{ + VB_Power(); +} + +int main() +{ + return 0; +} diff --git a/waterbox/vb/vb.h b/waterbox/vb/vb.h new file mode 100644 index 0000000000..8b26411e72 --- /dev/null +++ b/waterbox/vb/vb.h @@ -0,0 +1,114 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vb.h: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +#define MDFN_FASTCALL +#define INLINE inline +#define MDFN_COLD +#define NO_INLINE +//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align))) +#define MDFN_ASSUME_ALIGNED(p, align) (p) +#define trio_snprintf snprintf +#define TRUE true +#define FALSE false +#ifndef __alignas_is_defined +#define alignas(p) +#endif + +#include "endian.h" +#include "math_ops.h" +#include "blip/Blip_Buffer.h" +#include "v810/v810_fp_ops.h" +#include "v810/v810_cpu.h" + +#include "git.h" + +#include "vsu.h" +#include "vip.h" +#include "timer.h" +#include "input.h" + + +namespace MDFN_IEN_VB +{ + +enum +{ + VB3DMODE_ANAGLYPH = 0, + VB3DMODE_CSCOPE = 1, + VB3DMODE_SIDEBYSIDE = 2, + VB3DMODE_OVERUNDER = 3, + VB3DMODE_VLI, + VB3DMODE_HLI +}; + +#define VB_MASTER_CLOCK 20000000.0 + +enum +{ + VB_EVENT_VIP = 0, + VB_EVENT_TIMER, + VB_EVENT_INPUT, + // VB_EVENT_COMM +}; + +#define VB_EVENT_NONONO 0x7fffffff + +void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp); + +#define VBIRQ_SOURCE_INPUT 0 +#define VBIRQ_SOURCE_TIMER 1 +#define VBIRQ_SOURCE_EXPANSION 2 +#define VBIRQ_SOURCE_COMM 3 +#define VBIRQ_SOURCE_VIP 4 + +void VBIRQ_Assert(int source, bool assert); + +void VB_ExitLoop(void); + +void ForceEventUpdates(const v810_timestamp_t timestamp); + +uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t ×tamp, uint32 A); +uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t ×tamp, uint32 A); + +void MDFN_FASTCALL MemWrite8(v810_timestamp_t ×tamp, uint32 A, uint8 V); +void MDFN_FASTCALL MemWrite16(v810_timestamp_t ×tamp, uint32 A, uint16 V); + +extern int32 VB_InDebugPeek; +} diff --git a/waterbox/vb/vb.wbx b/waterbox/vb/vb.wbx new file mode 100644 index 0000000000..7cb3242652 Binary files /dev/null and b/waterbox/vb/vb.wbx differ diff --git a/waterbox/vb/vb.wbx.in b/waterbox/vb/vb.wbx.in new file mode 100644 index 0000000000..ab4e53bbe2 Binary files /dev/null and b/waterbox/vb/vb.wbx.in differ diff --git a/waterbox/vb/vip.cpp b/waterbox/vb/vip.cpp new file mode 100644 index 0000000000..31fc8dad66 --- /dev/null +++ b/waterbox/vb/vip.cpp @@ -0,0 +1,1399 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vip.cpp: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" +#include + +#define VIP_DBGMSG(format, ...) \ + { \ + } +//#define VIP_DBGMSG(format, ...) printf(format "\n", ## __VA_ARGS__) + +namespace MDFN_IEN_VB +{ + +static uint8 FB[2][2][0x6000]; +static uint16 CHR_RAM[0x8000 / sizeof(uint16)]; +static uint16 DRAM[0x20000 / sizeof(uint16)]; + +#define INT_SCAN_ERR 0x0001 +#define INT_LFB_END 0x0002 +#define INT_RFB_END 0x0004 +#define INT_GAME_START 0x0008 +#define INT_FRAME_START 0x0010 + +#define INT_SB_HIT 0x2000 +#define INT_XP_END 0x4000 +#define INT_TIME_ERR 0x8000 + +static uint16 InterruptPending; +static uint16 InterruptEnable; + +static uint8 BRTA, BRTB, BRTC, REST; +static uint8 Repeat; + +static void CopyFBColumnToTarget_Anaglyph(void) NO_INLINE; +static void CopyFBColumnToTarget_AnaglyphSlow(void) NO_INLINE; +static void CopyFBColumnToTarget_CScope(void) NO_INLINE; +static void CopyFBColumnToTarget_SideBySide(void) NO_INLINE; +static void CopyFBColumnToTarget_VLI(void) NO_INLINE; +static void CopyFBColumnToTarget_HLI(void) NO_INLINE; +static void (*CopyFBColumnToTarget)(void) = NULL; +static float VBLEDOnScale; +static uint32 VB3DMode; +static uint32 VB3DReverse; +static uint32 VBPrescale; +static uint32 VBSBS_Separation; +static uint32 HLILUT[256]; +static uint32 ColorLUT[2][256]; +static int32 BrightnessCache[4]; +static uint32 BrightCLUT[2][4]; + +static float ColorLUTNoGC[2][256][3]; +static uint32 AnaSlowColorLUT[256][256]; + +static bool VidSettingsDirty; +static bool ParallaxDisabled; +static uint32 Anaglyph_Colors[2]; +static uint32 Default_Color; + +static void MakeColorLUT() +{ + for (int lr = 0; lr < 2; lr++) + { + for (int i = 0; i < 256; i++) + { + float r, g, b; + uint32 modcolor_prime; + + if (VB3DMode == VB3DMODE_ANAGLYPH) + modcolor_prime = Anaglyph_Colors[lr ^ VB3DReverse]; + else + modcolor_prime = Default_Color; + + r = g = b = std::min(1.0, i * VBLEDOnScale / 255.0); + + // Modulate. + r = r * pow(((modcolor_prime >> 16) & 0xFF) / 255.0, 2.2 / 1.0); + g = g * pow(((modcolor_prime >> 8) & 0xFF) / 255.0, 2.2 / 1.0); + b = b * pow(((modcolor_prime >> 0) & 0xFF) / 255.0, 2.2 / 1.0); + + ColorLUTNoGC[lr][i][0] = r; + ColorLUTNoGC[lr][i][1] = g; + ColorLUTNoGC[lr][i][2] = b; + + // Apply gamma correction + const float r_prime = pow(r, 1.0 / 2.2); + const float g_prime = pow(g, 1.0 / 2.2); + const float b_prime = pow(b, 1.0 / 2.2); + + ColorLUT[lr][i] = (int)(r_prime * 255) & 0xff | (int)(g_prime * 255) << 8 & 0xff00 | (int)(b_prime * 255) << 16 & 0xff0000 | 0xff000000; + } + } + + // Anaglyph slow-mode LUT calculation + for (int l_b = 0; l_b < 256; l_b++) + { + for (int r_b = 0; r_b < 256; r_b++) + { + float r, g, b; + float r_prime, g_prime, b_prime; + + r = ColorLUTNoGC[0][l_b][0] + ColorLUTNoGC[1][r_b][0]; + g = ColorLUTNoGC[0][l_b][1] + ColorLUTNoGC[1][r_b][1]; + b = ColorLUTNoGC[0][l_b][2] + ColorLUTNoGC[1][r_b][2]; + + if (r > 1.0) + r = 1.0; + if (g > 1.0) + g = 1.0; + if (b > 1.0) + b = 1.0; + + r_prime = pow(r, 1.0 / 2.2); + g_prime = pow(g, 1.0 / 2.2); + b_prime = pow(b, 1.0 / 2.2); + + AnaSlowColorLUT[l_b][r_b] = (int)(r_prime * 255) & 0xff | (int)(g_prime * 255) << 8 & 0xff00 | (int)(b_prime * 255) << 16 & 0xff0000 | 0xff000000; + } + } +} + +static void RecalcBrightnessCache(void) +{ + static const int32 MaxTime = 255; + int32 CumulativeTime = (BRTA + 1 + BRTB + 1 + BRTC + 1 + REST + 1) + 1; + + //printf("BRTA: %d, BRTB: %d, BRTC: %d, Rest: %d --- %d\n", BRTA, BRTB, BRTC, REST, BRTA + 1 + BRTB + 1 + BRTC); + + BrightnessCache[0] = 0; + BrightnessCache[1] = 0; + BrightnessCache[2] = 0; + BrightnessCache[3] = 0; + + for (int i = 0; i < Repeat + 1; i++) + { + int32 btemp[4]; + + if ((i * CumulativeTime) >= MaxTime) + break; + + btemp[1] = (i * CumulativeTime) + BRTA; + if (btemp[1] > MaxTime) + btemp[1] = MaxTime; + btemp[1] -= (i * CumulativeTime); + if (btemp[1] < 0) + btemp[1] = 0; + + btemp[2] = (i * CumulativeTime) + BRTA + 1 + BRTB; + if (btemp[2] > MaxTime) + btemp[2] = MaxTime; + btemp[2] -= (i * CumulativeTime) + BRTA + 1; + if (btemp[2] < 0) + btemp[2] = 0; + + //btemp[3] = (i * CumulativeTime) + BRTA + 1 + BRTB + 1 + BRTC; + //if(btemp[3] > MaxTime) + // btemp[3] = MaxTime; + //btemp[3] -= (i * CumulativeTime); + //if(btemp[3] < 0) + // btemp[3] = 0; + + btemp[3] = (i * CumulativeTime) + BRTA + BRTB + BRTC + 1; + if (btemp[3] > MaxTime) + btemp[3] = MaxTime; + btemp[3] -= (i * CumulativeTime) + 1; + if (btemp[3] < 0) + btemp[3] = 0; + + BrightnessCache[1] += btemp[1]; + BrightnessCache[2] += btemp[2]; + BrightnessCache[3] += btemp[3]; + } + + //printf("BC: %d %d %d %d\n", BrightnessCache[0], BrightnessCache[1], BrightnessCache[2], BrightnessCache[3]); + + for (int lr = 0; lr < 2; lr++) + for (int i = 0; i < 4; i++) + { + BrightCLUT[lr][i] = ColorLUT[lr][BrightnessCache[i]]; + //printf("%d %d, %08x\n", lr, i, BrightCLUT[lr][i]); + } +} + +static void Recalc3DModeStuff(bool non_rgb_output = false) +{ + switch (VB3DMode) + { + default: + if (((Anaglyph_Colors[0] & 0xFF) && (Anaglyph_Colors[1] & 0xFF)) || + ((Anaglyph_Colors[0] & 0xFF00) && (Anaglyph_Colors[1] & 0xFF00)) || + ((Anaglyph_Colors[0] & 0xFF0000) && (Anaglyph_Colors[1] & 0xFF0000)) || + non_rgb_output) + { + CopyFBColumnToTarget = CopyFBColumnToTarget_AnaglyphSlow; + } + else + CopyFBColumnToTarget = CopyFBColumnToTarget_Anaglyph; + break; + + case VB3DMODE_CSCOPE: + CopyFBColumnToTarget = CopyFBColumnToTarget_CScope; + break; + + case VB3DMODE_SIDEBYSIDE: + CopyFBColumnToTarget = CopyFBColumnToTarget_SideBySide; + break; + + case VB3DMODE_VLI: + CopyFBColumnToTarget = CopyFBColumnToTarget_VLI; + break; + + case VB3DMODE_HLI: + CopyFBColumnToTarget = CopyFBColumnToTarget_HLI; + break; + } + RecalcBrightnessCache(); +} + +void VIP_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation) +{ + VB3DMode = mode; + VB3DReverse = reverse ? 1 : 0; + VBPrescale = prescale; + VBSBS_Separation = sbs_separation; + + VidSettingsDirty = true; + + for (uint32 p = 0; p < 256; p++) + { + uint32 v; + uint8 s[4]; + + s[0] = (p >> 0) & 0x3; + s[1] = (p >> 2) & 0x3; + s[2] = (p >> 4) & 0x3; + s[3] = (p >> 6) & 0x3; + + v = 0; + for (unsigned int i = 0, shifty = 0; i < 4; i++) + { + for (unsigned int ps = 0; ps < prescale; ps++) + { + v |= s[i] << shifty; + shifty += 2; + } + } + + HLILUT[p] = v; + } +} + +void VIP_SetParallaxDisable(bool disabled) +{ + ParallaxDisabled = disabled; +} + +void VIP_SetDefaultColor(uint32 default_color) +{ + Default_Color = default_color; + + VidSettingsDirty = true; +} + +void VIP_SetLEDOnScale(float coeff) +{ + VBLEDOnScale = coeff; +} + +void VIP_SetAnaglyphColors(uint32 lcolor, uint32 rcolor) +{ + Anaglyph_Colors[0] = lcolor; + Anaglyph_Colors[1] = rcolor; + + VidSettingsDirty = true; +} + +static uint16 FRMCYC; + +static uint16 DPCTRL; +static bool DisplayActive; + +#define XPCTRL_XP_RST 0x0001 +#define XPCTRL_XP_EN 0x0002 +static uint16 XPCTRL; +static uint16 SBCMP; // Derived from XPCTRL + +static uint16 SPT[4]; // SPT0~SPT3, 5f848~5f84e +static uint16 GPLT[4]; +static uint8 GPLT_Cache[4][4]; + +static INLINE void Recalc_GPLT_Cache(int which) +{ + for (int i = 0; i < 4; i++) + GPLT_Cache[which][i] = (GPLT[which] >> (i * 2)) & 3; +} + +static uint16 JPLT[4]; +static uint8 JPLT_Cache[4][4]; + +static INLINE void Recalc_JPLT_Cache(int which) +{ + for (int i = 0; i < 4; i++) + JPLT_Cache[which][i] = (JPLT[which] >> (i * 2)) & 3; +} + +static uint16 BKCOL; + +// +// +// +static int32 CalcNextEvent(void); + +static int32 last_ts; + +static uint32 Column; +static int32 ColumnCounter; + +static int32 DisplayRegion; +static bool DisplayFB; + +static int32 GameFrameCounter; + +static int32 DrawingCounter; +static bool DrawingActive; +static bool DrawingFB; +static uint32 DrawingBlock; +static int32 SB_Latch; +static int32 SBOUT_InactiveTime; + +//static uint8 CTA_L, CTA_R; + +static void CheckIRQ(void) +{ + VBIRQ_Assert(VBIRQ_SOURCE_VIP, (bool)(InterruptEnable & InterruptPending)); + +#if 0 + printf("%08x\n", InterruptEnable & InterruptPending); + if((bool)(InterruptEnable & InterruptPending)) + puts("IRQ asserted"); + else + puts("IRQ not asserted"); +#endif +} + +void VIP_Init(void) +{ + ParallaxDisabled = false; + Anaglyph_Colors[0] = 0xFF0000; + Anaglyph_Colors[1] = 0x0000FF; + VB3DMode = VB3DMODE_ANAGLYPH; + Default_Color = 0xFFFFFF; + VB3DReverse = 0; + VBPrescale = 1; + VBSBS_Separation = 0; + + VidSettingsDirty = true; +} + +void VIP_Kill(void) +{ +} + +void VIP_Power(void) +{ + Repeat = 0; + SB_Latch = 0; + SBOUT_InactiveTime = -1; + last_ts = 0; + + Column = 0; + ColumnCounter = 259; + + DisplayRegion = 0; + DisplayFB = 0; + + GameFrameCounter = 0; + + DrawingCounter = 0; + DrawingActive = false; + DrawingFB = 0; + DrawingBlock = 0; + + DPCTRL = 2; + DisplayActive = false; + + memset(FB, 0, 0x6000 * 2 * 2); + memset(CHR_RAM, 0, 0x8000); + memset(DRAM, 0, 0x20000); + + InterruptPending = 0; + InterruptEnable = 0; + + BRTA = 0; + BRTB = 0; + BRTC = 0; + REST = 0; + + FRMCYC = 0; + + XPCTRL = 0; + SBCMP = 0; + + for (int i = 0; i < 4; i++) + { + SPT[i] = 0; + GPLT[i] = 0; + JPLT[i] = 0; + + Recalc_GPLT_Cache(i); + Recalc_JPLT_Cache(i); + } + + BKCOL = 0; +} + +static INLINE uint16 ReadRegister(int32 ×tamp, uint32 A) +{ + uint16 ret = 0; //0xFFFF; + + if (A & 1) + VIP_DBGMSG("Misaligned VIP Read: %08x", A); + + switch (A & 0xFE) + { + default: + VIP_DBGMSG("Unknown VIP register read: %08x", A); + break; + + case 0x00: + ret = InterruptPending; + break; + + case 0x02: + ret = InterruptEnable; + break; + + case 0x20: //printf("Read DPSTTS at %d\n", timestamp); + ret = DPCTRL & 0x702; + if ((DisplayRegion & 1) && DisplayActive) + { + unsigned int DPBSY = 1 << ((DisplayRegion >> 1) & 1); + + if (DisplayFB) + DPBSY <<= 2; + + ret |= DPBSY << 2; + } + //if(!(DisplayRegion & 1)) // FIXME? (Had to do it this way for Galactic Pinball...) + ret |= 1 << 6; + break; + + // Note: Upper bits of BRTA, BRTB, BRTC, and REST(?) are 0 when read(on real hardware) + case 0x24: + ret = BRTA; + break; + + case 0x26: + ret = BRTB; + break; + + case 0x28: + ret = BRTC; + break; + + case 0x2A: + ret = REST; + break; + + case 0x30: + ret = 0xFFFF; + break; + + case 0x40: + ret = XPCTRL & 0x2; + if (DrawingActive) + { + ret |= (1 + DrawingFB) << 2; + } + if (timestamp < SBOUT_InactiveTime) + { + ret |= 0x8000; + ret |= /*DrawingBlock*/ SB_Latch << 8; + } + break; // XPSTTS, read-only + + case 0x44: + ret = 2; // VIP version. 2 is a known valid version, while the validity of other numbers is unknown, so we'll just go with 2. + break; + + case 0x48: + case 0x4a: + case 0x4c: + case 0x4e: + ret = SPT[(A >> 1) & 3]; + break; + + case 0x60: + case 0x62: + case 0x64: + case 0x66: + ret = GPLT[(A >> 1) & 3]; + break; + + case 0x68: + case 0x6a: + case 0x6c: + case 0x6e: + ret = JPLT[(A >> 1) & 3]; + break; + + case 0x70: + ret = BKCOL; + break; + } + + return (ret); +} + +static INLINE void WriteRegister(int32 ×tamp, uint32 A, uint16 V) +{ + if (A & 1) + VIP_DBGMSG("Misaligned VIP Write: %08x %04x", A, V); + + switch (A & 0xFE) + { + default: + VIP_DBGMSG("Unknown VIP register write: %08x %04x", A, V); + break; + + case 0x00: + break; // Interrupt pending, read-only + + case 0x02: + { + InterruptEnable = V & 0xE01F; + + VIP_DBGMSG("Interrupt Enable: %04x", V); + + if (V & 0x2000) + VIP_DBGMSG("Warning: VIP SB Hit Interrupt enable: %04x\n", V); + CheckIRQ(); + } + break; + + case 0x04: + InterruptPending &= ~V; + CheckIRQ(); + break; + + case 0x20: + break; // Display control, read-only. + + case 0x22: + DPCTRL = V & (0x703); // Display-control, write-only + if (V & 1) + { + DisplayActive = false; + InterruptPending &= ~(INT_TIME_ERR | INT_FRAME_START | INT_GAME_START | INT_RFB_END | INT_LFB_END | INT_SCAN_ERR); + CheckIRQ(); + } + break; + + case 0x24: + BRTA = V & 0xFF; // BRTA + RecalcBrightnessCache(); + break; + + case 0x26: + BRTB = V & 0xFF; // BRTB + RecalcBrightnessCache(); + break; + + case 0x28: + BRTC = V & 0xFF; // BRTC + RecalcBrightnessCache(); + break; + + case 0x2A: + REST = V & 0xFF; // REST + RecalcBrightnessCache(); + break; + + case 0x2E: + FRMCYC = V & 0xF; // FRMCYC, write-only? + break; + + case 0x30: + break; // CTA, read-only( + + case 0x40: + break; // XPSTTS, read-only + + case 0x42: + XPCTRL = V & 0x0002; // XPCTRL, write-only + SBCMP = (V >> 8) & 0x1F; + + if (V & 1) + { + VIP_DBGMSG("XPRST"); + DrawingActive = 0; + DrawingCounter = 0; + InterruptPending &= ~(INT_SB_HIT | INT_XP_END | INT_TIME_ERR); + CheckIRQ(); + } + break; + + case 0x44: + break; // Version Control, read-only? + + case 0x48: + case 0x4a: + case 0x4c: + case 0x4e: + SPT[(A >> 1) & 3] = V & 0x3FF; + break; + + case 0x60: + case 0x62: + case 0x64: + case 0x66: + GPLT[(A >> 1) & 3] = V & 0xFC; + Recalc_GPLT_Cache((A >> 1) & 3); + break; + + case 0x68: + case 0x6a: + case 0x6c: + case 0x6e: + JPLT[(A >> 1) & 3] = V & 0xFC; + Recalc_JPLT_Cache((A >> 1) & 3); + break; + + case 0x70: + BKCOL = V & 0x3; + break; + } +} + +// +// Don't update the VIP state on reads/writes, the event system will update it with enough precision as far as VB software cares. +// + +MDFN_FASTCALL uint8 VIP_Read8(int32 ×tamp, uint32 A) +{ + uint8 ret = 0; //0xFF; + + //VIP_Update(timestamp); + + switch (A >> 16) + { + case 0x0: + case 0x1: + if ((A & 0x7FFF) >= 0x6000) + { + ret = ne16_rbo_le(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000)); + } + else + { + ret = FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF]; + } + break; + + case 0x2: + case 0x3: + ret = ne16_rbo_le(DRAM, A & 0x1FFFF); + break; + + case 0x4: + case 0x5: + if (A >= 0x5E000) + ret = ReadRegister(timestamp, A); + else + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + + case 0x6: + break; + + case 0x7: + if (A >= 0x8000) + { + ret = ne16_rbo_le(CHR_RAM, A & 0x7FFF); + } + else + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + + default: + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + } + + //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent()); + + return (ret); +} + +MDFN_FASTCALL uint16 VIP_Read16(int32 ×tamp, uint32 A) +{ + uint16 ret = 0; //0xFFFF; + + //VIP_Update(timestamp); + + switch (A >> 16) + { + case 0x0: + case 0x1: + if ((A & 0x7FFF) >= 0x6000) + { + ret = ne16_rbo_le(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000)); + } + else + { + ret = MDFN_de16lsb(&FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF]); + } + break; + + case 0x2: + case 0x3: + ret = ne16_rbo_le(DRAM, A & 0x1FFFF); + break; + + case 0x4: + case 0x5: + if (A >= 0x5E000) + ret = ReadRegister(timestamp, A); + else + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + + case 0x6: + break; + + case 0x7: + if (A >= 0x8000) + { + ret = ne16_rbo_le(CHR_RAM, A & 0x7FFF); + } + else + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + + default: + VIP_DBGMSG("Unknown VIP Read: %08x", A); + break; + } + + //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent()); + return (ret); +} + +MDFN_FASTCALL void VIP_Write8(int32 ×tamp, uint32 A, uint8 V) +{ + //VIP_Update(timestamp); + + //if(A >= 0x3DC00 && A < 0x3E000) + // printf("%08x %02x\n", A, V); + + switch (A >> 16) + { + case 0x0: + case 0x1: + if ((A & 0x7FFF) >= 0x6000) + ne16_wbo_le(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000), V); + else + FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF] = V; + break; + + case 0x2: + case 0x3: + ne16_wbo_le(DRAM, A & 0x1FFFF, V); + break; + + case 0x4: + case 0x5: + if (A >= 0x5E000) + WriteRegister(timestamp, A, V); + else + VIP_DBGMSG("Unknown VIP Write: %08x %02x", A, V); + break; + + case 0x6: + VIP_DBGMSG("Unknown VIP Write: %08x %02x", A, V); + break; + + case 0x7: + if (A >= 0x8000) + ne16_wbo_le(CHR_RAM, A & 0x7FFF, V); + else + VIP_DBGMSG("Unknown VIP Write: %08x %02x", A, V); + break; + + default: + VIP_DBGMSG("Unknown VIP Write: %08x %02x", A, V); + break; + } + + //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent()); +} + +MDFN_FASTCALL void VIP_Write16(int32 ×tamp, uint32 A, uint16 V) +{ + //VIP_Update(timestamp); + + //if(A >= 0x3DC00 && A < 0x3E000) + // printf("%08x %04x\n", A, V); + + switch (A >> 16) + { + case 0x0: + case 0x1: + if ((A & 0x7FFF) >= 0x6000) + ne16_wbo_le(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000), V); + else + MDFN_en16lsb(&FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF], V); + break; + + case 0x2: + case 0x3: + ne16_wbo_le(DRAM, A & 0x1FFFF, V); + break; + + case 0x4: + case 0x5: + if (A >= 0x5E000) + WriteRegister(timestamp, A, V); + else + VIP_DBGMSG("Unknown VIP Write: %08x %04x", A, V); + break; + + case 0x6: + VIP_DBGMSG("Unknown VIP Write: %08x %04x", A, V); + break; + + case 0x7: + if (A >= 0x8000) + ne16_wbo_le(CHR_RAM, A & 0x7FFF, V); + else + VIP_DBGMSG("Unknown VIP Write: %08x %04x", A, V); + break; + + default: + VIP_DBGMSG("Unknown VIP Write: %08x %04x", A, V); + break; + } + + //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent()); +} + +static MDFN_Surface real_surface; +static MDFN_Surface *surface; + +void VIP_StartFrame(EmulateSpecStruct *espec) +{ + // puts("Start frame"); + + if (VidSettingsDirty) + { + MakeColorLUT(); + Recalc3DModeStuff(); + + VidSettingsDirty = false; + } + + espec->DisplayRect.x = 0; + espec->DisplayRect.y = 0; + + switch (VB3DMode) + { + default: + espec->DisplayRect.w = 384; + espec->DisplayRect.h = 224; + break; + + case VB3DMODE_VLI: + espec->DisplayRect.w = 768 * VBPrescale; + espec->DisplayRect.h = 224; + break; + + case VB3DMODE_HLI: + espec->DisplayRect.w = 384; + espec->DisplayRect.h = 448 * VBPrescale; + break; + + case VB3DMODE_CSCOPE: + espec->DisplayRect.w = 512; + espec->DisplayRect.h = 384; + break; + + case VB3DMODE_SIDEBYSIDE: + espec->DisplayRect.w = 768 + VBSBS_Separation; + espec->DisplayRect.h = 224; + break; + } + + surface = &real_surface; + real_surface.pixels = espec->pixels; + real_surface.pitch32 = espec->DisplayRect.w; +} + +void VIP_ResetTS(void) +{ + if (SBOUT_InactiveTime >= 0) + SBOUT_InactiveTime -= last_ts; + last_ts = 0; +} + +static int32 CalcNextEvent(void) +{ + return (ColumnCounter); +} + +#include "vip_draw.inc" + +static INLINE void CopyFBColumnToTarget_Anaglyph_BASE(const bool DisplayActive_arg, const int lr) +{ + const int fb = DisplayFB; + uint32 *target = surface->pixels + Column; + const int32 pitch32 = surface->pitch32; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + uint32 pixel = BrightCLUT[lr][source_bits & 3]; + + if (!DisplayActive_arg) + pixel = 0; + + if (lr) + *target |= pixel; + else + *target = pixel; + + source_bits >>= 2; + target += pitch32; + } + fb_source++; + } +} + +static void CopyFBColumnToTarget_Anaglyph(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_Anaglyph_BASE(0, 0); + else + CopyFBColumnToTarget_Anaglyph_BASE(0, 1); + } + else + { + if (!lr) + CopyFBColumnToTarget_Anaglyph_BASE(1, 0); + else + CopyFBColumnToTarget_Anaglyph_BASE(1, 1); + } +} + +static uint32 AnaSlowBuf[384][224]; + +static INLINE void CopyFBColumnToTarget_AnaglyphSlow_BASE(const bool DisplayActive_arg, const int lr) +{ + const int fb = DisplayFB; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + if (!lr) + { + uint32 *target = AnaSlowBuf[Column]; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + uint32 pixel = BrightnessCache[source_bits & 3]; + + if (!DisplayActive_arg) + pixel = 0; + + *target = pixel; + source_bits >>= 2; + target++; + } + fb_source++; + } + } + else + { + uint32 *target = surface->pixels + Column; + const uint32 *left_src = AnaSlowBuf[Column]; + const int32 pitch32 = surface->pitch32; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + uint32 pixel = AnaSlowColorLUT[*left_src][DisplayActive_arg ? BrightnessCache[source_bits & 3] : 0]; + + *target = pixel; + + source_bits >>= 2; + target += pitch32; + left_src++; + } + fb_source++; + } + } +} + +static void CopyFBColumnToTarget_AnaglyphSlow(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_AnaglyphSlow_BASE(0, 0); + else + CopyFBColumnToTarget_AnaglyphSlow_BASE(0, 1); + } + else + { + if (!lr) + CopyFBColumnToTarget_AnaglyphSlow_BASE(1, 0); + else + CopyFBColumnToTarget_AnaglyphSlow_BASE(1, 1); + } +} + +static void CopyFBColumnToTarget_CScope_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr) +{ + const int fb = DisplayFB; + uint32 *target = surface->pixels + (dest_lr ? 512 - 16 - 1 : 16) + (dest_lr ? Column : 383 - Column) * surface->pitch32; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + if (DisplayActive_arg) + *target = BrightCLUT[lr][source_bits & 3]; + else + *target = 0; + + source_bits >>= 2; + if (dest_lr) + target--; + else + target++; + } + fb_source++; + } +} + +static void CopyFBColumnToTarget_CScope(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_CScope_BASE(0, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_CScope_BASE(0, 1, 1 ^ VB3DReverse); + } + else + { + if (!lr) + CopyFBColumnToTarget_CScope_BASE(1, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_CScope_BASE(1, 1, 1 ^ VB3DReverse); + } +} + +static void CopyFBColumnToTarget_SideBySide_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr) +{ + const int fb = DisplayFB; + uint32 *target = surface->pixels + Column + (dest_lr ? (384 + VBSBS_Separation) : 0); + const int32 pitch32 = surface->pitch32; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + if (DisplayActive_arg) + *target = BrightCLUT[lr][source_bits & 3]; + else + *target = 0; + source_bits >>= 2; + target += pitch32; + } + fb_source++; + } +} + +static void CopyFBColumnToTarget_SideBySide(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_SideBySide_BASE(0, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_SideBySide_BASE(0, 1, 1 ^ VB3DReverse); + } + else + { + if (!lr) + CopyFBColumnToTarget_SideBySide_BASE(1, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_SideBySide_BASE(1, 1, 1 ^ VB3DReverse); + } +} + +static INLINE void CopyFBColumnToTarget_VLI_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr) +{ + const int fb = DisplayFB; + uint32 *target = surface->pixels + Column * 2 * VBPrescale + dest_lr; + const int32 pitch32 = surface->pitch32; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + uint32 tv; + + if (DisplayActive_arg) + tv = BrightCLUT[lr][source_bits & 3]; + else + tv = 0; + + for (uint32 ps = 0; ps < VBPrescale; ps++) + target[ps * 2] = tv; + + source_bits >>= 2; + target += pitch32; + } + fb_source++; + } +} + +static void CopyFBColumnToTarget_VLI(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_VLI_BASE(0, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_VLI_BASE(0, 1, 1 ^ VB3DReverse); + } + else + { + if (!lr) + CopyFBColumnToTarget_VLI_BASE(1, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_VLI_BASE(1, 1, 1 ^ VB3DReverse); + } +} + +static INLINE void CopyFBColumnToTarget_HLI_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr) +{ + const int fb = DisplayFB; + const int32 pitch32 = surface->pitch32; + uint32 *target = surface->pixels + Column + dest_lr * pitch32; + const uint8 *fb_source = &FB[fb][lr][64 * Column]; + + if (VBPrescale <= 4) + for (int y = 56; y; y--) + { + uint32 source_bits = HLILUT[*fb_source]; + + for (int y_sub = 4 * VBPrescale; y_sub; y_sub--) + { + if (DisplayActive_arg) + *target = BrightCLUT[lr][source_bits & 3]; + else + *target = 0; + + target += pitch32 * 2; + source_bits >>= 2; + } + fb_source++; + } + else + for (int y = 56; y; y--) + { + uint32 source_bits = *fb_source; + + for (int y_sub = 4; y_sub; y_sub--) + { + for (uint32 ps = 0; ps < VBPrescale; ps++) + { + if (DisplayActive_arg) + *target = BrightCLUT[lr][source_bits & 3]; + else + *target = 0; + + target += pitch32 * 2; + } + + source_bits >>= 2; + } + fb_source++; + } +} + +static void CopyFBColumnToTarget_HLI(void) +{ + const int lr = (DisplayRegion & 2) >> 1; + + if (!DisplayActive) + { + if (!lr) + CopyFBColumnToTarget_HLI_BASE(0, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_HLI_BASE(0, 1, 1 ^ VB3DReverse); + } + else + { + if (!lr) + CopyFBColumnToTarget_HLI_BASE(1, 0, 0 ^ VB3DReverse); + else + CopyFBColumnToTarget_HLI_BASE(1, 1, 1 ^ VB3DReverse); + } +} + +v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp) +{ + int32 clocks = timestamp - last_ts; + int32 running_timestamp = timestamp; + + while (clocks > 0) + { + int32 chunk_clocks = clocks; + + if (DrawingCounter > 0 && chunk_clocks > DrawingCounter) + chunk_clocks = DrawingCounter; + if (chunk_clocks > ColumnCounter) + chunk_clocks = ColumnCounter; + + running_timestamp += chunk_clocks; + + if (DrawingCounter > 0) + { + DrawingCounter -= chunk_clocks; + if (DrawingCounter <= 0) + { + alignas(8) uint8 DrawingBuffers[2][512 * 8]; // Don't decrease this from 512 unless you adjust vip_draw.inc(including areas that draw off-visible >= 384 and >= -7 for speed reasons) + + VIP_DrawBlock(DrawingBlock, DrawingBuffers[0] + 8, DrawingBuffers[1] + 8); + + for (int lr = 0; lr < 2; lr++) + { + uint8 *FB_Target = FB[DrawingFB][lr] + DrawingBlock * 2; + + for (int x = 0; x < 384; x++) + { + FB_Target[64 * x + 0] = (DrawingBuffers[lr][8 + x + 512 * 0] << 0) | (DrawingBuffers[lr][8 + x + 512 * 1] << 2) | (DrawingBuffers[lr][8 + x + 512 * 2] << 4) | (DrawingBuffers[lr][8 + x + 512 * 3] << 6); + FB_Target[64 * x + 1] = (DrawingBuffers[lr][8 + x + 512 * 4] << 0) | (DrawingBuffers[lr][8 + x + 512 * 5] << 2) | (DrawingBuffers[lr][8 + x + 512 * 6] << 4) | (DrawingBuffers[lr][8 + x + 512 * 7] << 6); + } + } + + SBOUT_InactiveTime = running_timestamp + 1120; + SB_Latch = DrawingBlock; // Not exactly correct, but probably doesn't matter. + + DrawingBlock++; + if (DrawingBlock == 28) + { + DrawingActive = false; + + InterruptPending |= INT_XP_END; + CheckIRQ(); + } + else + DrawingCounter += 1120 * 4; + } + } + + ColumnCounter -= chunk_clocks; + if (ColumnCounter == 0) + { + if (DisplayRegion & 1) + { + if (!(Column & 3)) + { + const int lr = (DisplayRegion & 2) >> 1; + uint16 ctdata = ne16_rbo_le(DRAM, 0x1DFFE - ((Column >> 2) * 2) - (lr ? 0 : 0x200)); + + //printf("%02x, repeat: %02x\n", ctdata & 0xFF, ctdata >> 8); + + if ((ctdata >> 8) != Repeat) + { + Repeat = ctdata >> 8; + RecalcBrightnessCache(); + } + } + CopyFBColumnToTarget(); + } + + ColumnCounter = 259; + Column++; + if (Column == 384) + { + Column = 0; + + if (DisplayActive) + { + if (DisplayRegion & 1) // Did we just finish displaying an active region? + { + if (DisplayRegion & 2) // finished displaying right eye + InterruptPending |= INT_RFB_END; + else // Otherwise, left eye + InterruptPending |= INT_LFB_END; + + CheckIRQ(); + } + } + + DisplayRegion = (DisplayRegion + 1) & 3; + + if (DisplayRegion == 0) // New frame start + { + DisplayActive = DPCTRL & 0x2; + + if (DisplayActive) + { + InterruptPending |= INT_FRAME_START; + CheckIRQ(); + } + GameFrameCounter++; + if (GameFrameCounter > FRMCYC) // New game frame start? + { + InterruptPending |= INT_GAME_START; + CheckIRQ(); + + if (XPCTRL & XPCTRL_XP_EN) + { + DisplayFB ^= 1; + + DrawingBlock = 0; + DrawingActive = true; + DrawingCounter = 1120 * 4; + DrawingFB = DisplayFB ^ 1; + } + + GameFrameCounter = 0; + } + + VB_ExitLoop(); + } + } + } + + clocks -= chunk_clocks; + } + + last_ts = timestamp; + + return (timestamp + CalcNextEvent()); +} +} diff --git a/waterbox/vb/vip.h b/waterbox/vb/vip.h new file mode 100644 index 0000000000..61bc1c59aa --- /dev/null +++ b/waterbox/vb/vip.h @@ -0,0 +1,83 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vip.h: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#pragma once + +namespace MDFN_IEN_VB +{ +void VIP_Init(void) MDFN_COLD; +void VIP_Kill(void) MDFN_COLD; +void VIP_Power(void) MDFN_COLD; + +void VIP_SetInstantDisplayHack(bool) MDFN_COLD; +void VIP_SetAllowDrawSkip(bool) MDFN_COLD; +void VIP_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation) MDFN_COLD; +void VIP_SetParallaxDisable(bool disabled) MDFN_COLD; +void VIP_SetDefaultColor(uint32 default_color) MDFN_COLD; +void VIP_SetAnaglyphColors(uint32 lcolor, uint32 rcolor) MDFN_COLD; // R << 16, G << 8, B << 0 +void VIP_SetLEDOnScale(float coeff) MDFN_COLD; + +v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp); +void VIP_ResetTS(void); + +void VIP_StartFrame(EmulateSpecStruct *espec); + +MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t ×tamp, uint32 A); +MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t ×tamp, uint32 A); + +MDFN_FASTCALL void VIP_Write8(v810_timestamp_t ×tamp, uint32 A, uint8 V); +MDFN_FASTCALL void VIP_Write16(v810_timestamp_t ×tamp, uint32 A, uint16 V); + +enum +{ + VIP_GSREG_IPENDING = 0, // Current pending interrupt(bits) + VIP_GSREG_IENABLE, + + VIP_GSREG_DPCTRL, + + VIP_GSREG_BRTA, + VIP_GSREG_BRTB, + VIP_GSREG_BRTC, + VIP_GSREG_REST, + VIP_GSREG_FRMCYC, + VIP_GSREG_XPCTRL, + + VIP_GSREG_SPT0, + VIP_GSREG_SPT1, + VIP_GSREG_SPT2, + VIP_GSREG_SPT3, + + VIP_GSREG_GPLT0, + VIP_GSREG_GPLT1, + VIP_GSREG_GPLT2, + VIP_GSREG_GPLT3, + + VIP_GSREG_JPLT0, + VIP_GSREG_JPLT1, + VIP_GSREG_JPLT2, + VIP_GSREG_JPLT3, + + VIP_GSREG_BKCOL, +}; + +uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len); +void VIP_SetRegister(const unsigned int id, const uint32 value); +} diff --git a/waterbox/vb/vip_draw.inc b/waterbox/vb/vip_draw.inc new file mode 100644 index 0000000000..f7eb222589 --- /dev/null +++ b/waterbox/vb/vip_draw.inc @@ -0,0 +1,493 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vip_draw.inc: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#define BGM_AFFINE 0x2 +#define BGM_OBJ 0x3 + + +static void DrawBG(uint8 *target, uint16 RealY, bool lr, uint8 bgmap_base_raw, bool overplane, uint16 overplane_char, uint32 SourceX, uint32 SourceY, uint32 scx, uint32 scy, uint16 DestX, uint16 DestY, uint16 DestWidth, uint16 DestHeight) +{ + const uint16 *CHR16 = CHR_RAM; + const uint16 *BGMap = DRAM; + uint32 BGMap_Base = bgmap_base_raw << 12; + int32 start_x, final_x; + const uint32 bgsc_overplane = DRAM[overplane_char]; + const uint32 BGMap_XCount = 1 << scx; + const uint32 BGMap_YCount = 1 << scy; + const uint32 SourceX_Size = 512 * BGMap_XCount; + const uint32 SourceY_Size = 512 * BGMap_YCount; + const uint32 SourceX_Mask = overplane ? 0x1FFF : (SourceX_Size - 1); + const uint32 SourceY_Mask = overplane ? 0x1FFF : (SourceY_Size - 1); + + if((uint16)(RealY - DestY) > DestHeight) + return; + + //printf("%d, %d, %d, %d\n", overplane, srcXSize, srcYSize, bgmap_base_raw); + + DestX = sign_10_to_s16(DestX); + + if(DestX & 0x8000) + SourceX -= DestX; + + start_x = (int16)DestX; + final_x = (int16)DestX + DestWidth; + + if(start_x < 0) + start_x = 0; + + if(final_x > 383) + final_x = 383; + + if(start_x > final_x) + return; + + // Optimization: + SourceY &= SourceY_Mask; + BGMap_Base |= (((SourceY >> 3) & 0x3F) * 0x40) | (((SourceY << 3) & ~0xFFF) << scx); + + for(int x = start_x; x <= final_x; x++) + { + uint32 bgsc; + uint32 char_no; + uint32 palette_selector; + uint32 hflip_xor; + uint32 vflip_xor; + + SourceX &= SourceX_Mask; + + bgsc = bgsc_overplane; + + if(SourceX < SourceX_Size && SourceY < SourceY_Size) + bgsc = BGMap[(BGMap_Base | ((SourceX << 3) & ~0xFFF) | ((SourceX >> 3) & 0x3F)) & 0xFFFF]; + + char_no = bgsc & 0x7FF; + palette_selector = bgsc >> 14; + hflip_xor = (bgsc & 0x2000) ? 7 : 0; //(((int32)bgsc << 18) >> 31) & 0x7; + vflip_xor = (bgsc & 0x1000) ? 7 : 0; //(((int32)bgsc << 19) >> 31) & 0x7; + + unsigned int char_sub_y = vflip_xor ^ (SourceY & 0x7); + + if(!(SourceX & 7) && (x + 7) <= final_x) + { + uint32 pixels = CHR16[char_no * 8 + char_sub_y]; + + #if 0 + unsigned int char_sub_x; + uint8 *sub_target = target + x + 8; + + for(int sub_x = -8; sub_x < 0; sub_x++) + { + if(pixels & 3) sub_target[sub_x] = GPLT_Cache[palette_selector][pixels & 3]; + pixels >>= 2; + } + #endif + + if(bgsc & 0x2000) + { + if((pixels >> 14) & 3) target[0 + x] = GPLT_Cache[palette_selector][(pixels >> 14) & 3]; + if((pixels >> 12) & 3) target[1 + x] = GPLT_Cache[palette_selector][(pixels >> 12) & 3]; + if((pixels >> 10) & 3) target[2 + x] = GPLT_Cache[palette_selector][(pixels >> 10) & 3]; + if((pixels >> 8) & 3) target[3 + x] = GPLT_Cache[palette_selector][(pixels >> 8) & 3]; + if((pixels >> 6) & 3) target[4 + x] = GPLT_Cache[palette_selector][(pixels >> 6) & 3]; + if((pixels >> 4) & 3) target[5 + x] = GPLT_Cache[palette_selector][(pixels >> 4) & 3]; + if((pixels >> 2) & 3) target[6 + x] = GPLT_Cache[palette_selector][(pixels >> 2) & 3]; + if((pixels >> 0) & 3) target[7 + x] = GPLT_Cache[palette_selector][(pixels >> 0) & 3]; + } + else + { + if((pixels >> 0) & 3) target[0 + x] = GPLT_Cache[palette_selector][(pixels >> 0) & 3]; + if((pixels >> 2) & 3) target[1 + x] = GPLT_Cache[palette_selector][(pixels >> 2) & 3]; + if((pixels >> 4) & 3) target[2 + x] = GPLT_Cache[palette_selector][(pixels >> 4) & 3]; + if((pixels >> 6) & 3) target[3 + x] = GPLT_Cache[palette_selector][(pixels >> 6) & 3]; + if((pixels >> 8) & 3) target[4 + x] = GPLT_Cache[palette_selector][(pixels >> 8) & 3]; + if((pixels >> 10) & 3) target[5 + x] = GPLT_Cache[palette_selector][(pixels >> 10) & 3]; + if((pixels >> 12) & 3) target[6 + x] = GPLT_Cache[palette_selector][(pixels >> 12) & 3]; + if((pixels >> 14) & 3) target[7 + x] = GPLT_Cache[palette_selector][(pixels >> 14) & 3]; + } + + x += 7; + SourceX += 8; + } + else + { + unsigned int char_sub_x; + + char_sub_x = hflip_xor ^ (SourceX & 0x7); + + uint8 pixel = (CHR16[char_no * 8 + char_sub_y] >> (char_sub_x * 2)) & 0x3; + + if(pixel) + target[x] = GPLT_Cache[palette_selector][pixel]; //target[x] = (GPLT[palette_selector] >> (pixel * 2)) & 0x3; + SourceX++; + } + } +} + +static void DrawAffine(uint8 *target, uint16 RealY, bool lr, uint32 ParamBase, uint32 BGMap_Base, bool OverplaneMode, uint16 OverplaneChar, uint32 scx, uint32 scy, + uint16 DestX, uint16 DestY, uint16 DestWidth, uint16 DestHeight) +{ + const uint16 *CHR16 = CHR_RAM; + const uint16 *BGMap = DRAM; + + const uint32 BGMap_XCount = 1 << scx; + const uint32 BGMap_YCount = 1 << scy; + const uint32 SourceX_Size = 512 * BGMap_XCount; + const uint32 SourceY_Size = 512 * BGMap_YCount; + + const uint16 *param_ptr = &DRAM[(ParamBase + 8 * (RealY - DestY)) & 0xFFFF]; + int16 mx = param_ptr[0], mp = (ParallaxDisabled ? 0 : param_ptr[1]), my = param_ptr[2], dx = param_ptr[3], dy = param_ptr[4]; + + uint32 SourceX, SourceY; + uint32 SourceX_Mask, SourceY_Mask; + + int32 start_x, final_x; + const uint32 bgsc_overplane = DRAM[OverplaneChar]; + + + DestX = sign_10_to_s16(DestX); + + if((uint16)(RealY - DestY) > DestHeight) + return; + + SourceX = (int32)mx << 6; + SourceY = (int32)my << 6; + + if(DestX & 0x8000) + { + SourceX += dx * (65536 - DestX); + SourceY += dy * (65536 - DestX); + } + + if(mp >= 0 && lr) + { + SourceX += dx * mp; + SourceY += dy * mp; + } + else if(mp < 0 && !lr) + { + SourceX += dx * -mp; + SourceY += dy * -mp; + } + + if(OverplaneMode) + { + SourceX_Mask = 0x3FFFFFF; //(((uint32)SourceX_Size << 9) * 2) - 1; + SourceY_Mask = 0x3FFFFFF; //(((uint32)SourceY_Size << 9) * 2) - 1; + } + else + { + SourceX_Mask = ((uint32)SourceX_Size << 9) - 1; + SourceY_Mask = ((uint32)SourceY_Size << 9) - 1; + } + + start_x = (int16)DestX; + final_x = (int16)DestX + DestWidth; + + if(start_x < 0) + start_x = 0; + + if(final_x > 383) + final_x = 383; + +if(dy == 0) // Optimization for no rotation. +{ + SourceY &= SourceY_Mask; + + if(SourceY >= (SourceY_Size << 9)) + return; + + BGMap_Base |= (((SourceY >> 6) & ~0xFFF) << scx) | (((SourceY >> 12) & 0x3F) * 0x40); + for(int x = start_x; x <= final_x; x++) + { + uint32 bgsc; + uint32 hflip_xor; + uint32 vflip_xor; + uint32 pixel = 0; + + SourceX &= SourceX_Mask; + + bgsc = bgsc_overplane; + + if(SourceX < (SourceX_Size << 9)) + bgsc = BGMap[(BGMap_Base | ((SourceX >> 6) & ~0xFFF) | ((SourceX >> 12) & 0x3F)) & 0xFFFF]; + + //hflip_xor = bgsc & 0x2000 ? 0xE : 0; + //vflip_xor = bgsc & 0x1000 ? 0x7 : 0; + hflip_xor = ((int32)(bgsc << 18) >> 30) & 0xE; + vflip_xor = ((int32)(bgsc << 19) >> 31) & 0x7; + + unsigned int char_sub_y = vflip_xor ^ ((SourceY >> 9) & 0x7); + unsigned int char_sub_x = hflip_xor ^ ((SourceX >> 8) & 0xE); + + pixel = (CHR16[((bgsc & 0x7FF) * 8) | char_sub_y] >> char_sub_x) & 0x3; + + if(pixel) + target[x] = GPLT_Cache[bgsc >> 14][pixel]; + + SourceX += dx; + } +} +else + for(int x = start_x; x <= final_x; x++) + { + uint32 bgsc; + uint32 char_no; + uint32 palette_selector; + uint32 hflip_xor; + uint32 vflip_xor; + uint8 pixel = 0; + + SourceX &= SourceX_Mask; + SourceY &= SourceY_Mask; + + bgsc = bgsc_overplane; + + if(SourceX < (SourceX_Size << 9) && SourceY < (SourceY_Size << 9)) + { + uint32 m_index = ((SourceX >> 6) & ~0xFFF) + (((SourceY >> 6) & ~0xFFF) << scx); + uint32 sub_index = ((SourceX >> 12) & 0x3F) + (((SourceY >> 12) & 0x3F) * 0x40); + + bgsc = BGMap[(BGMap_Base | m_index | sub_index) & 0xFFFF]; + + //bgsc = BGMap[(BGMapBase + (SourceX >> 12) + (SourceY >> 12) * (SourceX_Size >> 3)) & 0xFFFF ]; + } + char_no = bgsc & 0x7FF; + palette_selector = bgsc >> 14; + hflip_xor = bgsc & 0x2000 ? 7 : 0; //(((int32)bgsc << 18) >> 31) & 0x7; + vflip_xor = bgsc & 0x1000 ? 7 : 0; //(((int32)bgsc << 19) >> 31) & 0x7; + + unsigned int char_sub_y = vflip_xor ^ ((SourceY >> 9) & 0x7); + unsigned int char_sub_x = hflip_xor ^ ((SourceX >> 9) & 0x7); + + pixel = (CHR16[char_no * 8 + char_sub_y] >> (char_sub_x * 2)) & 0x3; + + if(pixel) + target[x] = GPLT_Cache[palette_selector][pixel]; + + SourceX += dx; + SourceY += dy; + } +} + +static int obj_search_which; + +static void DrawOBJ(uint8 *fb[2], uint16 Y, bool lron[2]) +{ + const uint16 *CHR16 = CHR_RAM; + + int32 start_oam; + int32 end_oam; + + start_oam = SPT[obj_search_which]; + + end_oam = 1023; + if(obj_search_which) + end_oam = SPT[obj_search_which - 1]; + + int32 oam = start_oam; + do + { + const uint16 *oam_ptr = &DRAM[(0x1E000 + (oam * 8)) >> 1]; + const uint32 jy = oam_ptr[2]; + const uint32 tile_y = (Y - jy) & 0xFF; // I think this mask is right. See: http://www.planetvb.com/modules/newbb/viewtopic.php?topic_id=3797&forum=2 + + if(tile_y >= 8) + continue; + + uint32 jx = oam_ptr[0]; + uint32 jp = ParallaxDisabled ? 0 : (oam_ptr[1] & 0x3FFF); + uint32 palette_selector = oam_ptr[3] >> 14; + uint32 vflip_xor = (oam_ptr[3] & 0x1000) ? 7 : 0; + uint32 char_sub_y = vflip_xor ^ tile_y; + bool jlron[2] = { (bool)(oam_ptr[1] & 0x8000), (bool)(oam_ptr[1] & 0x4000) }; + uint32 char_no = oam_ptr[3] & 0x7FF; + const uint32 pixels_save = CHR16[char_no * 8 + char_sub_y]; + + for(int lr = 0; lr < 2; lr++) + { + if(!(jlron[lr] & lron[lr])) + continue; + + uint32 pixels = pixels_save; + int32 x = sign_x_to_s32(10, (jx + (lr ? jp : -jp))); // It may actually be 9, TODO? + + if(x >= -7 && x < 384) // Make sure we always keep the pitch of our 384x8 buffer large enough(with padding before and after the visible space) + { + uint8 *target = &fb[lr][x]; + + if(oam_ptr[3] & 0x2000) + { + target += 7; + + for(int meow = 8; meow; meow--) + { + if(pixels & 3) + *target = JPLT_Cache[palette_selector][pixels & 3]; + target--; + pixels >>= 2; + } + } + else + { + for(int meow = 8; meow; meow--) + { + if(pixels & 3) + *target = JPLT_Cache[palette_selector][pixels & 3]; + target++; + pixels >>= 2; + } + } + #if 0 + if(oam_ptr[3] & 0x2000) + { + if((pixels >> 14) & 3) fb[lr][0 + x] = JPLT_Cache[palette_selector][(pixels >> 14) & 3]; + if((pixels >> 12) & 3) fb[lr][1 + x] = JPLT_Cache[palette_selector][(pixels >> 12) & 3]; + if((pixels >> 10) & 3) fb[lr][2 + x] = JPLT_Cache[palette_selector][(pixels >> 10) & 3]; + if((pixels >> 8) & 3) fb[lr][3 + x] = JPLT_Cache[palette_selector][(pixels >> 8) & 3]; + if((pixels >> 6) & 3) fb[lr][4 + x] = JPLT_Cache[palette_selector][(pixels >> 6) & 3]; + if((pixels >> 4) & 3) fb[lr][5 + x] = JPLT_Cache[palette_selector][(pixels >> 4) & 3]; + if((pixels >> 2) & 3) fb[lr][6 + x] = JPLT_Cache[palette_selector][(pixels >> 2) & 3]; + if((pixels >> 0) & 3) fb[lr][7 + x] = JPLT_Cache[palette_selector][(pixels >> 0) & 3]; + } + else + { + if((pixels >> 0) & 3) fb[lr][0 + x] = JPLT_Cache[palette_selector][(pixels >> 0) & 3]; + if((pixels >> 2) & 3) fb[lr][1 + x] = JPLT_Cache[palette_selector][(pixels >> 2) & 3]; + if((pixels >> 4) & 3) fb[lr][2 + x] = JPLT_Cache[palette_selector][(pixels >> 4) & 3]; + if((pixels >> 6) & 3) fb[lr][3 + x] = JPLT_Cache[palette_selector][(pixels >> 6) & 3]; + if((pixels >> 8) & 3) fb[lr][4 + x] = JPLT_Cache[palette_selector][(pixels >> 8) & 3]; + if((pixels >> 10) & 3) fb[lr][5 + x] = JPLT_Cache[palette_selector][(pixels >> 10) & 3]; + if((pixels >> 12) & 3) fb[lr][6 + x] = JPLT_Cache[palette_selector][(pixels >> 12) & 3]; + if((pixels >> 14) & 3) fb[lr][7 + x] = JPLT_Cache[palette_selector][(pixels >> 14) & 3]; + } +#endif + + } + + } + } while( (oam = (oam - 1) & 1023) != end_oam); + +} + + +void VIP_DrawBlock(uint8 block_no, uint8 *fb_l, uint8 *fb_r) +{ + for(int y = 0; y < 8; y++) + { + memset(fb_l + y * 512, BKCOL, 384); + memset(fb_r + y * 512, BKCOL, 384); + } + + obj_search_which = 3; + + for(int world = 31; world >= 0; world--) + { + const uint16 *world_ptr = &DRAM[(0x1D800 + world * 0x20) >> 1]; + + uint32 bgmap_base = world_ptr[0] & 0xF; + bool end = world_ptr[0] & 0x40; + bool over = world_ptr[0] & 0x80; + uint32 scy = (world_ptr[0] >> 8) & 3; + uint32 scx = (world_ptr[0] >> 10) & 3; + uint32 bgm = (world_ptr[0] >> 12) & 3; + bool lron[2] = { (bool)(world_ptr[0] & 0x8000), (bool)(world_ptr[0] & 0x4000) }; + + uint16 gx = sign_11_to_s16(world_ptr[1]); + uint16 gp = ParallaxDisabled ? 0 : sign_9_to_s16(world_ptr[2]); + uint16 gy = sign_11_to_s16(world_ptr[3]); + uint16 mx = world_ptr[4]; + uint16 mp = ParallaxDisabled ? 0 : sign_9_to_s16(world_ptr[5]); + uint16 my = world_ptr[6]; + uint16 window_width = sign_11_to_s16(world_ptr[7]); + uint16 window_height = (world_ptr[8] & 0x3FF); + uint32 param_base = (world_ptr[9] & 0xFFF0); + uint16 overplane_char = world_ptr[10]; + + if(end) + break; + + if(((512 << scx) + (512 << scy)) > 4096) + { + printf("BG Size too large for world: %d(scx=%d, scy=%d)\n", world, scx, scy); + } + +// if(world != 2) +// continue; + + // if(block_no == 8) + // printf("World: %d; gx: %d, gp: %d, gy: %d, mx: %d, mp: %d, my: %d, window_width: %d, window_height: %d\n", world, gx, gp, gy, mx, mp, my, window_width, window_height); + + for(int y = 0; y < 8; y++) + { + uint8 *fb[2] = { &fb_l[y * 512], &fb_r[y * 512] }; + + if(bgm == BGM_OBJ) + { + if(!lron[0] || !lron[1]) + printf("Bad OBJ World? %d(%d/%d) %d~%d\n", world, lron[0], lron[1], SPT[obj_search_which], obj_search_which ? (SPT[obj_search_which - 1] + 1) : 0); + + DrawOBJ(fb, (block_no * 8) + y, lron); + } + else if(bgm == BGM_AFFINE) + { + //if(((block_no * 8) + y) == 128) + // printf("Draw affine: %d %d\n", gx, gp); + for(int lr = 0; lr < 2; lr++) + { + if(lron[lr]) + { + DrawAffine(fb[lr], (block_no * 8) + y, lr, param_base, bgmap_base * 4096, over, overplane_char, scx, scy, + gx + (lr ? gp : -gp), gy, window_width, window_height); + } + } + } + else + for(int lr = 0; lr < 2; lr++) + { + uint16 srcX, srcY; + uint16 RealY = (block_no * 8) + y; + uint16 DestX; + uint16 DestY; + + srcX = mx + (lr ? mp : -mp); + srcY = my + (RealY - gy); + + DestX = gx + (lr ? gp : -gp); + DestY = gy; + + if(lron[lr]) + { + if(bgm == 1) // HBias + srcX += (int16)DRAM[(param_base + (((RealY - DestY) * 2) | lr)) & 0xFFFF]; + + DrawBG(fb[lr], RealY, lr, bgmap_base, over, overplane_char, (int32)(int16)srcX, (int32)(int16)srcY, scx, scy, DestX, DestY, window_width, window_height); + } + } + } + + if(bgm == BGM_OBJ) + if(obj_search_which) + obj_search_which--; + + } + + +} diff --git a/waterbox/vb/vsu.cpp b/waterbox/vb/vsu.cpp new file mode 100644 index 0000000000..d99b254509 --- /dev/null +++ b/waterbox/vb/vsu.cpp @@ -0,0 +1,498 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vsu.cpp: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vb.h" + +static const unsigned int Tap_LUT[8] = {15 - 1, 11 - 1, 14 - 1, 5 - 1, 9 - 1, 7 - 1, 10 - 1, 12 - 1}; + +VSU::VSU() +{ + Synth.volume(1.0 / 6 / 2); + + for (int ch = 0; ch < 6; ch++) + { + for (int lr = 0; lr < 2; lr++) + last_output[ch][lr] = 0; + } +} + +VSU::~VSU() +{ +} + +void VSU::SetSoundRate(double rate) +{ + for (int y = 0; y < 2; y++) + { + sbuf[y].set_sample_rate(rate ? rate : 44100, 50); + sbuf[y].clock_rate((long)(VB_MASTER_CLOCK / 4)); + sbuf[y].bass_freq(20); + } +} + +void VSU::Power(void) +{ + SweepControl = 0; + SweepModCounter = 0; + SweepModClockDivider = 1; + + for (int ch = 0; ch < 6; ch++) + { + IntlControl[ch] = 0; + LeftLevel[ch] = 0; + RightLevel[ch] = 0; + Frequency[ch] = 0; + EnvControl[ch] = 0; + RAMAddress[ch] = 0; + + EffFreq[ch] = 0; + Envelope[ch] = 0; + WavePos[ch] = 0; + FreqCounter[ch] = 1; + IntervalCounter[ch] = 0; + EnvelopeCounter[ch] = 1; + + EffectsClockDivider[ch] = 4800; + IntervalClockDivider[ch] = 4; + EnvelopeClockDivider[ch] = 4; + + LatcherClockDivider[ch] = 120; + } + + NoiseLatcherClockDivider = 120; + NoiseLatcher = 0; + + memset(WaveData, 0, sizeof(WaveData)); + memset(ModData, 0, sizeof(ModData)); + + last_ts = 0; +} + +void VSU::Write(int32 timestamp, uint32 A, uint8 V) +{ + A &= 0x7FF; + + Update(timestamp); + + //printf("VSU Write: %d, %08x %02x\n", timestamp, A, V); + + if (A < 0x280) + WaveData[A >> 7][(A >> 2) & 0x1F] = V & 0x3F; + else if (A < 0x400) + { + //if(A >= 0x300) + // printf("Modulation mirror write? %08x %02x\n", A, V); + ModData[(A >> 2) & 0x1F] = V; + } + else if (A < 0x600) + { + int ch = (A >> 6) & 0xF; + + //if(ch < 6) + //printf("Ch: %d, Reg: %d, Value: %02x\n", ch, (A >> 2) & 0xF, V); + + if (ch > 5) + { + if (A == 0x580 && (V & 1)) + { + //puts("STOP, HAMMER TIME"); + for (int i = 0; i < 6; i++) + IntlControl[i] &= ~0x80; + } + } + else + switch ((A >> 2) & 0xF) + { + case 0x0: + IntlControl[ch] = V & ~0x40; + + if (V & 0x80) + { + EffFreq[ch] = Frequency[ch]; + if (ch == 5) + FreqCounter[ch] = 10 * (2048 - EffFreq[ch]); + else + FreqCounter[ch] = 2048 - EffFreq[ch]; + IntervalCounter[ch] = (V & 0x1F) + 1; + EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1; + + if (ch == 4) + { + SweepModCounter = (SweepControl >> 4) & 7; + SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1; + ModWavePos = 0; + } + + WavePos[ch] = 0; + + if (ch == 5) // Not sure if this is correct. + lfsr = 1; + + //if(!(IntlControl[ch] & 0x80)) + // Envelope[ch] = (EnvControl[ch] >> 4) & 0xF; + + EffectsClockDivider[ch] = 4800; + IntervalClockDivider[ch] = 4; + EnvelopeClockDivider[ch] = 4; + } + break; + + case 0x1: + LeftLevel[ch] = (V >> 4) & 0xF; + RightLevel[ch] = (V >> 0) & 0xF; + break; + + case 0x2: + Frequency[ch] &= 0xFF00; + Frequency[ch] |= V << 0; + EffFreq[ch] &= 0xFF00; + EffFreq[ch] |= V << 0; + break; + + case 0x3: + Frequency[ch] &= 0x00FF; + Frequency[ch] |= (V & 0x7) << 8; + EffFreq[ch] &= 0x00FF; + EffFreq[ch] |= (V & 0x7) << 8; + break; + + case 0x4: + EnvControl[ch] &= 0xFF00; + EnvControl[ch] |= V << 0; + + Envelope[ch] = (V >> 4) & 0xF; + break; + + case 0x5: + EnvControl[ch] &= 0x00FF; + if (ch == 4) + EnvControl[ch] |= (V & 0x73) << 8; + else if (ch == 5) + { + EnvControl[ch] |= (V & 0x73) << 8; + lfsr = 1; + } + else + EnvControl[ch] |= (V & 0x03) << 8; + break; + + case 0x6: + RAMAddress[ch] = V & 0xF; + break; + + case 0x7: + if (ch == 4) + { + SweepControl = V; + } + break; + } + } +} + +INLINE void VSU::CalcCurrentOutput(int ch, int &left, int &right) +{ + if (!(IntlControl[ch] & 0x80)) + { + left = right = 0; + return; + } + + int WD; + int l_ol, r_ol; + + if (ch == 5) + WD = NoiseLatcher; //(NoiseLatcher << 6) - NoiseLatcher; + else + { + if (RAMAddress[ch] > 4) + WD = 0; + else + WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20; + } + l_ol = Envelope[ch] * LeftLevel[ch]; + if (l_ol) + { + l_ol >>= 3; + l_ol += 1; + } + + r_ol = Envelope[ch] * RightLevel[ch]; + if (r_ol) + { + r_ol >>= 3; + r_ol += 1; + } + + left = WD * l_ol; + right = WD * r_ol; +} + +void VSU::Update(int32 timestamp) +{ + //puts("VSU Start"); + int left, right; + + for (int ch = 0; ch < 6; ch++) + { + int32 clocks = timestamp - last_ts; + int32 running_timestamp = last_ts; + + // Output sound here + CalcCurrentOutput(ch, left, right); + Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]); + Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]); + last_output[ch][0] = left; + last_output[ch][1] = right; + + if (!(IntlControl[ch] & 0x80)) + continue; + + while (clocks > 0) + { + int32 chunk_clocks = clocks; + + if (chunk_clocks > EffectsClockDivider[ch]) + chunk_clocks = EffectsClockDivider[ch]; + + if (ch == 5) + { + if (chunk_clocks > NoiseLatcherClockDivider) + chunk_clocks = NoiseLatcherClockDivider; + } + else + { + if (EffFreq[ch] >= 2040) + { + if (chunk_clocks > LatcherClockDivider[ch]) + chunk_clocks = LatcherClockDivider[ch]; + } + else + { + if (chunk_clocks > FreqCounter[ch]) + chunk_clocks = FreqCounter[ch]; + } + } + + if (ch == 5 && chunk_clocks > NoiseLatcherClockDivider) + chunk_clocks = NoiseLatcherClockDivider; + + FreqCounter[ch] -= chunk_clocks; + while (FreqCounter[ch] <= 0) + { + if (ch == 5) + { + int feedback = ((lfsr >> 7) & 1) ^ ((lfsr >> Tap_LUT[(EnvControl[5] >> 12) & 0x7]) & 1) ^ 1; + lfsr = ((lfsr << 1) & 0x7FFF) | feedback; + + FreqCounter[ch] += 10 * (2048 - EffFreq[ch]); + } + else + { + FreqCounter[ch] += 2048 - EffFreq[ch]; + WavePos[ch] = (WavePos[ch] + 1) & 0x1F; + } + } + + LatcherClockDivider[ch] -= chunk_clocks; + while (LatcherClockDivider[ch] <= 0) + LatcherClockDivider[ch] += 120; + + if (ch == 5) + { + NoiseLatcherClockDivider -= chunk_clocks; + if (!NoiseLatcherClockDivider) + { + NoiseLatcherClockDivider = 120; + NoiseLatcher = ((lfsr & 1) << 6) - (lfsr & 1); + } + } + + EffectsClockDivider[ch] -= chunk_clocks; + while (EffectsClockDivider[ch] <= 0) + { + EffectsClockDivider[ch] += 4800; + + IntervalClockDivider[ch]--; + while (IntervalClockDivider[ch] <= 0) + { + IntervalClockDivider[ch] += 4; + + if (IntlControl[ch] & 0x20) + { + IntervalCounter[ch]--; + if (!IntervalCounter[ch]) + { + IntlControl[ch] &= ~0x80; + } + } + + EnvelopeClockDivider[ch]--; + while (EnvelopeClockDivider[ch] <= 0) + { + EnvelopeClockDivider[ch] += 4; + + if (EnvControl[ch] & 0x0100) // Enveloping enabled? + { + EnvelopeCounter[ch]--; + if (!EnvelopeCounter[ch]) + { + EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1; + + if (EnvControl[ch] & 0x0008) // Grow + { + if (Envelope[ch] < 0xF || (EnvControl[ch] & 0x200)) + Envelope[ch] = (Envelope[ch] + 1) & 0xF; + } + else // Decay + { + if (Envelope[ch] > 0 || (EnvControl[ch] & 0x200)) + Envelope[ch] = (Envelope[ch] - 1) & 0xF; + } + } + } + + } // end while(EnvelopeClockDivider[ch] <= 0) + } // end while(IntervalClockDivider[ch] <= 0) + + if (ch == 4) + { + SweepModClockDivider--; + while (SweepModClockDivider <= 0) + { + SweepModClockDivider += (SweepControl & 0x80) ? 8 : 1; + + if (((SweepControl >> 4) & 0x7) && (EnvControl[ch] & 0x4000)) + { + if (SweepModCounter) + SweepModCounter--; + + if (!SweepModCounter) + { + SweepModCounter = (SweepControl >> 4) & 0x7; + + if (EnvControl[ch] & 0x1000) // Modulation + { + if (ModWavePos < 32 || (EnvControl[ch] & 0x2000)) + { + ModWavePos &= 0x1F; + + EffFreq[ch] = (EffFreq[ch] + (int8)ModData[ModWavePos]); + if (EffFreq[ch] < 0) + { + //puts("Underflow"); + EffFreq[ch] = 0; + } + else if (EffFreq[ch] > 0x7FF) + { + //puts("Overflow"); + EffFreq[ch] = 0x7FF; + } + ModWavePos++; + } + //puts("Mod"); + } + else // Sweep + { + int32 delta = EffFreq[ch] >> (SweepControl & 0x7); + int32 NewFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta); + + //printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq); + + if (NewFreq < 0) + EffFreq[ch] = 0; + else if (NewFreq > 0x7FF) + { + //EffFreq[ch] = 0x7FF; + IntlControl[ch] &= ~0x80; + } + else + EffFreq[ch] = NewFreq; + } + } + } + } // end while(SweepModClockDivider <= 0) + } // end if(ch == 4) + } // end while(EffectsClockDivider[ch] <= 0) + clocks -= chunk_clocks; + running_timestamp += chunk_clocks; + + // Output sound here too. + CalcCurrentOutput(ch, left, right); + Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]); + Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]); + last_output[ch][0] = left; + last_output[ch][1] = right; + } + } + last_ts = timestamp; + //puts("VSU End"); +} + +int32 VSU::EndFrame(int32 timestamp, int16 *SoundBuf, int32 SoundBufMaxSize) +{ + int32 ret = 0; + + Update(timestamp); + last_ts = 0; + + if (SoundBuf) + { + for (int y = 0; y < 2; y++) + { + sbuf[y].end_frame(timestamp); + ret = sbuf[y].read_samples(SoundBuf + y, SoundBufMaxSize, 1); + } + } + + return ret; +} + +uint8 VSU::PeekWave(const unsigned int which, uint32 Address) +{ + assert(which <= 4); + + Address &= 0x1F; + + return (WaveData[which][Address]); +} + +void VSU::PokeWave(const unsigned int which, uint32 Address, uint8 value) +{ + assert(which <= 4); + + Address &= 0x1F; + + WaveData[which][Address] = value & 0x3F; +} + +uint8 VSU::PeekModWave(uint32 Address) +{ + Address &= 0x1F; + return (ModData[Address]); +} + +void VSU::PokeModWave(uint32 Address, uint8 value) +{ + Address &= 0x1F; + + ModData[Address] = value & 0xFF; +} diff --git a/waterbox/vb/vsu.h b/waterbox/vb/vsu.h new file mode 100644 index 0000000000..c8230beb85 --- /dev/null +++ b/waterbox/vb/vsu.h @@ -0,0 +1,93 @@ +/******************************************************************************/ +/* Mednafen Virtual Boy Emulation Module */ +/******************************************************************************/ +/* vsu.h: +** Copyright (C) 2010-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#pragma once + +class VSU +{ + public: + VSU() + MDFN_COLD; + ~VSU() MDFN_COLD; + + void SetSoundRate(double rate) MDFN_COLD; + + void Power(void) MDFN_COLD; + + void Write(int32 timestamp, uint32 A, uint8 V); + + int32 EndFrame(int32 timestamp, int16 *SoundBuf, int32 SoundBufMaxSize); + + uint8 PeekWave(const unsigned int which, uint32 Address); + void PokeWave(const unsigned int which, uint32 Address, uint8 value); + + uint8 PeekModWave(uint32 Address); + void PokeModWave(uint32 Address, uint8 value); + + private: + void CalcCurrentOutput(int ch, int &left, int &right); + + void Update(int32 timestamp); + + uint8 IntlControl[6]; + uint8 LeftLevel[6]; + uint8 RightLevel[6]; + uint16 Frequency[6]; + uint16 EnvControl[6]; // Channel 5/6 extra functionality tacked on too. + + uint8 RAMAddress[6]; + + uint8 SweepControl; + + uint8 WaveData[5][0x20]; + + uint8 ModData[0x20]; + + int32 EffFreq[6]; + int32 Envelope[6]; + + int32 WavePos[6]; + int32 ModWavePos; + + int32 LatcherClockDivider[6]; + + int32 FreqCounter[6]; + int32 IntervalCounter[6]; + int32 EnvelopeCounter[6]; + int32 SweepModCounter; + + int32 EffectsClockDivider[6]; + int32 IntervalClockDivider[6]; + int32 EnvelopeClockDivider[6]; + int32 SweepModClockDivider; + + int32 NoiseLatcherClockDivider; + uint32 NoiseLatcher; + + uint32 lfsr; + + int32 last_output[6][2]; + int32 last_ts; + + Blip_Buffer sbuf[2]; + Blip_Synth Synth; + Blip_Synth NoiseSynth; +};