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;
+};