From 23e0ca1b1fc2d92d534201d9b2899de6f9fd5967 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" <Jake.Stine@96395faa-99c1-11dd-bbfe-3dabce05a288> Date: Fri, 6 Mar 2009 01:11:17 +0000 Subject: [PATCH] Added googlecode's sparsehash / densehash classes, which may or may not be useful in the future. Removed various instances of legacy VM code that is no longer needed for reference purposes. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@695 96395faa-99c1-11dd-bbfe-3dabce05a288 --- 3rdparty/google/dense_hash_map | 261 +++ 3rdparty/google/dense_hash_set | 245 ++ 3rdparty/google/sparse_hash_map | 246 ++ 3rdparty/google/sparse_hash_set | 231 ++ 3rdparty/google/sparsehash/densehashtable.h | 986 ++++++++ 3rdparty/google/sparsehash/sparseconfig.h | 74 + 3rdparty/google/sparsehash/sparsehashtable.h | 941 ++++++++ 3rdparty/google/sparsetable | 1451 ++++++++++++ 3rdparty/google/type_traits.h | 250 ++ pcsx2/Hw.cpp | 6 +- pcsx2/MemoryVM.cpp | 2140 ------------------ pcsx2/SPR.cpp | 4 +- pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 52 +- pcsx2/windows/WinVM.cpp | 470 ---- pcsx2/x86/iGS.cpp | 298 --- pcsx2/x86/iHw.cpp | 1145 ---------- pcsx2/x86/iIPU.cpp | 203 -- pcsx2/x86/iPsxHw.cpp | 1179 ---------- pcsx2/x86/iR5900CoissuedLoadStore.cpp | 1738 -------------- 19 files changed, 4698 insertions(+), 7222 deletions(-) create mode 100644 3rdparty/google/dense_hash_map create mode 100644 3rdparty/google/dense_hash_set create mode 100644 3rdparty/google/sparse_hash_map create mode 100644 3rdparty/google/sparse_hash_set create mode 100644 3rdparty/google/sparsehash/densehashtable.h create mode 100644 3rdparty/google/sparsehash/sparseconfig.h create mode 100644 3rdparty/google/sparsehash/sparsehashtable.h create mode 100644 3rdparty/google/sparsetable create mode 100644 3rdparty/google/type_traits.h delete mode 100644 pcsx2/MemoryVM.cpp delete mode 100644 pcsx2/windows/WinVM.cpp delete mode 100644 pcsx2/x86/iGS.cpp delete mode 100644 pcsx2/x86/iHw.cpp delete mode 100644 pcsx2/x86/iIPU.cpp delete mode 100644 pcsx2/x86/iPsxHw.cpp delete mode 100644 pcsx2/x86/iR5900CoissuedLoadStore.cpp diff --git a/3rdparty/google/dense_hash_map b/3rdparty/google/dense_hash_map new file mode 100644 index 0000000000..41a749a493 --- /dev/null +++ b/3rdparty/google/dense_hash_map @@ -0,0 +1,261 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ---- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over densehashtable.h, just +// like sgi stl's stl_hash_map is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// NOTE: this is exactly like sparse_hash_map.h, with the word +// "sparse" replaced by "dense", except for the addition of +// set_empty_key(). +// +// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. +// +// Otherwise your program will die in mysterious ways. +// +// In other respects, we adhere mostly to the STL semantics for +// hash-map. One important exception is that insert() invalidates +// iterators entirely. On the plus side, though, erase() doesn't +// invalidate iterators at all, or even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// If you want to use erase() you must call set_deleted_key(), +// in addition to set_empty_key(), after construction. +// The deleted and empty keys must differ. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// +// 3) set_resizing_parameters(0.0, 0.8): +// Setting the shrink_resize_percent to 0.0 guarantees +// that the hash table will never shrink. +// +// Guide to what kind of hash_map to use: +// (1) dense_hash_map: fastest, uses the most memory +// (2) sparse_hash_map: slowest, uses the least memory +// (3) hash_map (STL): in the middle +// Typically I use sparse_hash_map when I care about space and/or when +// I need to save the hashtable on disk. I use hash_map otherwise. I +// don't personally use dense_hash_map ever; the only use of +// dense_hash_map I know of is to work around malloc() bugs in some +// systems (dense_hash_map has a particularly simple allocation scheme). +// +// - dense_hash_map has, typically, a factor of 2 memory overhead (if your +// data takes up X bytes, the hash_map uses X more bytes in overhead). +// - sparse_hash_map has about 2 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_map.html +// for information about how to use this class. + +#ifndef _DENSE_HASH_MAP_H_ +#define _DENSE_HASH_MAP_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <stdio.h> // for FILE * in read()/write() +#include <algorithm> // for the default template args +#include <functional> // for equal_to +#include <memory> // for alloc<> +#include <utility> // for pair<> +#include HASH_FUN_H // defined in config.h +#include <google/sparsehash/densehashtable.h> + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template <class Key, class T, + class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to<Key>, + class Alloc = STL_NAMESPACE::allocator<T> > +class dense_hash_map { + private: + // Apparently select1st is not stl-standard, so we define our own + struct SelectKey { + const Key& operator()(const pair<const Key, T>& p) const { + return p.first; + } + }; + + // The actual data + typedef dense_hashtable<pair<const Key, T>, Key, HashFcn, + SelectKey, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef T data_type; + typedef T mapped_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::iterator iterator; + typedef typename ht::const_iterator const_iterator; + + // Iterator functions + iterator begin() { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator begin() const { return rep.begin(); } + const_iterator end() const { return rep.end(); } + + + // Accessor functions + hasher hash_funct() const { return rep.hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit dense_hash_map(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { } + + template <class InputIterator> + dense_hash_map(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + // This clears the hash map without resizing it down to the minimum + // bucket count, but rather keeps the number of buckets constant + void clear_no_resize() { rep.clear_no_resize(); } + void swap(dense_hash_map& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + void resize(size_type hint) { rep.resize(hint); } + + void set_resizing_parameters(float shrink, float grow) { + return rep.set_resizing_parameters(shrink, grow); + } + + // Lookup routines + iterator find(const key_type& key) { return rep.find(key); } + const_iterator find(const key_type& key) const { return rep.find(key); } + + data_type& operator[](const key_type& key) { // This is our value-add! + iterator it = find(key); + if (it != end()) { + return it->second; + } else { + return insert(value_type(key, data_type())).first->second; + } + } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair<iterator, iterator> equal_range(const key_type& key) { + return rep.equal_range(key); + } + pair<const_iterator, const_iterator> equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); } + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion and empty routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted and empty buckets. You can change the + // deleted key as time goes on, or get rid of it entirely to be insert-only. + void set_empty_key(const key_type& key) { // YOU MUST CALL THIS! + rep.set_empty_key(value_type(key, data_type())); // rep wants a value + } + void set_deleted_key(const key_type& key) { + rep.set_deleted_key(value_type(key, data_type())); // rep wants a value + } + void clear_deleted_key() { rep.clear_deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; } + bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +// We need a global swap as well +template <class Key, class T, class HashFcn, class EqualKey, class Alloc> +inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1, + dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) { + hm1.swap(hm2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSE_HASH_MAP_H_ */ diff --git a/3rdparty/google/dense_hash_set b/3rdparty/google/dense_hash_set new file mode 100644 index 0000000000..30e2c64065 --- /dev/null +++ b/3rdparty/google/dense_hash_set @@ -0,0 +1,245 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over densehashtable.h, just +// like sgi stl's stl_hash_set is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// This is more different from dense_hash_map than you might think, +// because all iterators for sets are const (you obviously can't +// change the key, and for sets there is no value). +// +// NOTE: this is exactly like sparse_hash_set.h, with the word +// "sparse" replaced by "dense", except for the addition of +// set_empty_key(). +// +// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. +// +// Otherwise your program will die in mysterious ways. +// +// In other respects, we adhere mostly to the STL semantics for +// hash-set. One important exception is that insert() invalidates +// iterators entirely. On the plus side, though, erase() doesn't +// invalidate iterators at all, or even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// If you want to use erase() you must call set_deleted_key(), +// in addition to set_empty_key(), after construction. +// The deleted and empty keys must differ. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// +// 3) set_resizing_parameters(0.0, 0.8): +// Setting the shrink_resize_percent to 0.0 guarantees +// that the hash table will never shrink. +// +// Guide to what kind of hash_set to use: +// (1) dense_hash_set: fastest, uses the most memory +// (2) sparse_hash_set: slowest, uses the least memory +// (3) hash_set (STL): in the middle +// Typically I use sparse_hash_set when I care about space and/or when +// I need to save the hashtable on disk. I use hash_set otherwise. I +// don't personally use dense_hash_set ever; the only use of +// dense_hash_set I know of is to work around malloc() bugs in some +// systems (dense_hash_set has a particularly simple allocation scheme). +// +// - dense_hash_set has, typically, a factor of 2 memory overhead (if your +// data takes up X bytes, the hash_set uses X more bytes in overhead). +// - sparse_hash_set has about 2 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_set.html +// for information about how to use this class. + +#ifndef _DENSE_HASH_SET_H_ +#define _DENSE_HASH_SET_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <stdio.h> // for FILE * in read()/write() +#include <algorithm> // for the default template args +#include <functional> // for equal_to +#include <memory> // for alloc<> +#include <utility> // for pair<> +#include HASH_FUN_H // defined in config.h +#include <google/sparsehash/densehashtable.h> + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template <class Value, + class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to<Value>, + class Alloc = STL_NAMESPACE::allocator<Value> > +class dense_hash_set { + private: + // Apparently identity is not stl-standard, so we define our own + struct Identity { + Value& operator()(Value& v) const { return v; } + const Value& operator()(const Value& v) const { return v; } + }; + + // The actual data + typedef dense_hashtable<Value, Value, HashFcn, Identity, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::const_pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::const_reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::const_iterator iterator; + typedef typename ht::const_iterator const_iterator; + + + // Iterator functions -- recall all iterators are const + iterator begin() const { return rep.begin(); } + iterator end() const { return rep.end(); } + + + // Accessor functions + hasher hash_funct() const { return rep.hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit dense_hash_set(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { } + + template <class InputIterator> + dense_hash_set(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + // This clears the hash set without resizing it down to the minimum + // bucket count, but rather keeps the number of buckets constant + void clear_no_resize() { rep.clear_no_resize(); } + void swap(dense_hash_set& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + void resize(size_type hint) { rep.resize(hint); } + + void set_resizing_parameters(float shrink, float grow) { + return rep.set_resizing_parameters(shrink, grow); + } + + // Lookup routines + iterator find(const key_type& key) const { return rep.find(key); } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair<iterator, iterator> equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair<iterator, bool> insert(const value_type& obj) { + pair<typename ht::iterator, bool> p = rep.insert(obj); + return pair<iterator, bool>(p.first, p.second); // const to non-const + } + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion and empty routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted and empty buckets. You can change the + // deleted key as time goes on, or get rid of it entirely to be insert-only. + void set_empty_key(const key_type& key) { rep.set_empty_key(key); } + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; } + bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +template <class Val, class HashFcn, class EqualKey, class Alloc> +inline void swap(dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1, + dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) { + hs1.swap(hs2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSE_HASH_SET_H_ */ diff --git a/3rdparty/google/sparse_hash_map b/3rdparty/google/sparse_hash_map new file mode 100644 index 0000000000..18f699218e --- /dev/null +++ b/3rdparty/google/sparse_hash_map @@ -0,0 +1,246 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over sparsehashtable.h, just +// like sgi stl's stl_hash_map is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// We adhere mostly to the STL semantics for hash-map. One important +// exception is that insert() invalidates iterators entirely. On the +// plus side, though, delete() doesn't invalidate iterators at all, or +// even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// Unlike STL's hash_map, if you want to use erase() you +// must call set_deleted_key() after construction. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This is what allows you to iterate over a hashtable +// and call erase() without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// +// 3) set_resizing_parameters(0.0, 0.8): +// Setting the shrink_resize_percent to 0.0 guarantees +// that the hash table will never shrink. +// +// Guide to what kind of hash_map to use: +// (1) dense_hash_map: fastest, uses the most memory +// (2) sparse_hash_map: slowest, uses the least memory +// (3) hash_map (STL): in the middle +// Typically I use sparse_hash_map when I care about space and/or when +// I need to save the hashtable on disk. I use hash_map otherwise. I +// don't personally use dense_hash_map ever; the only use of +// dense_hash_map I know of is to work around malloc() bugs in some +// systems (dense_hash_map has a particularly simple allocation scheme). +// +// - dense_hash_map has, typically, a factor of 2 memory overhead (if your +// data takes up X bytes, the hash_map uses X more bytes in overhead). +// - sparse_hash_map has about 2 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-0.1/sparse_hash_map.html +// for information about how to use this class. + +#ifndef _SPARSE_HASH_MAP_H_ +#define _SPARSE_HASH_MAP_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <stdio.h> // for FILE * in read()/write() +#include <algorithm> // for the default template args +#include <functional> // for equal_to +#include <memory> // for alloc<> +#include <utility> // for pair<> +#include HASH_FUN_H // defined in config.h +#include <google/sparsehash/sparsehashtable.h> + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template <class Key, class T, + class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to<Key>, + class Alloc = STL_NAMESPACE::allocator<T> > +class sparse_hash_map { + private: + // Apparently select1st is not stl-standard, so we define our own + struct SelectKey { + const Key& operator()(const pair<const Key, T>& p) const { + return p.first; + } + }; + + // The actual data + typedef sparse_hashtable<pair<const Key, T>, Key, HashFcn, + SelectKey, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef T data_type; + typedef T mapped_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::iterator iterator; + typedef typename ht::const_iterator const_iterator; + + // Iterator functions + iterator begin() { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator begin() const { return rep.begin(); } + const_iterator end() const { return rep.end(); } + + + // Accessor functions + hasher hash_funct() const { return rep.hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit sparse_hash_map(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { } + + template <class InputIterator> + sparse_hash_map(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + void swap(sparse_hash_map& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + void resize(size_type hint) { rep.resize(hint); } + + void set_resizing_parameters(float shrink, float grow) { + return rep.set_resizing_parameters(shrink, grow); + } + + // Lookup routines + iterator find(const key_type& key) { return rep.find(key); } + const_iterator find(const key_type& key) const { return rep.find(key); } + + data_type& operator[](const key_type& key) { // This is our value-add! + iterator it = find(key); + if (it != end()) { + return it->second; + } else { + return insert(value_type(key, data_type())).first->second; + } + } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair<iterator, iterator> equal_range(const key_type& key) { + return rep.equal_range(key); + } + pair<const_iterator, const_iterator> equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); } + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted buckets. You can change the key as + // time goes on, or get rid of it entirely to be insert-only. + void set_deleted_key(const key_type& key) { + rep.set_deleted_key(value_type(key, data_type())); // rep wants a value + } + void clear_deleted_key() { rep.clear_deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +// We need a global swap as well +template <class Key, class T, class HashFcn, class EqualKey, class Alloc> +inline void swap(sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1, + sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) { + hm1.swap(hm2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSE_HASH_MAP_H_ */ diff --git a/3rdparty/google/sparse_hash_set b/3rdparty/google/sparse_hash_set new file mode 100644 index 0000000000..3c268ef564 --- /dev/null +++ b/3rdparty/google/sparse_hash_set @@ -0,0 +1,231 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over sparsehashtable.h, just +// like sgi stl's stl_hash_set is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// This is more different from sparse_hash_map than you might think, +// because all iterators for sets are const (you obviously can't +// change the key, and for sets there is no value). +// +// We adhere mostly to the STL semantics for hash-set. One important +// exception is that insert() invalidates iterators entirely. On the +// plus side, though, delete() doesn't invalidate iterators at all, or +// even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// Unlike STL's hash_map, if you want to use erase() you +// must call set_deleted_key() after construction. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// +// 3) set_resizing_parameters(0.0, 0.8): +// Setting the shrink_resize_percent to 0.0 guarantees +// that the hash table will never shrink. +// +// Guide to what kind of hash_set to use: +// (1) dense_hash_set: fastest, uses the most memory +// (2) sparse_hash_set: slowest, uses the least memory +// (3) hash_set (STL): in the middle +// Typically I use sparse_hash_set when I care about space and/or when +// I need to save the hashtable on disk. I use hash_set otherwise. I +// don't personally use dense_hash_set ever; the only use of +// dense_hash_set I know of is to work around malloc() bugs in some +// systems (dense_hash_set has a particularly simple allocation scheme). +// +// - dense_hash_set has, typically, a factor of 2 memory overhead (if your +// data takes up X bytes, the hash_set uses X more bytes in overhead). +// - sparse_hash_set has about 2 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-0.1/sparse_hash_set.html +// for information about how to use this class. + +#ifndef _SPARSE_HASH_SET_H_ +#define _SPARSE_HASH_SET_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <stdio.h> // for FILE * in read()/write() +#include <algorithm> // for the default template args +#include <functional> // for equal_to +#include <memory> // for alloc<> +#include <utility> // for pair<> +#include HASH_FUN_H // defined in config.h +#include <google/sparsehash/sparsehashtable.h> + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template <class Value, + class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to<Value>, + class Alloc = STL_NAMESPACE::allocator<Value> > +class sparse_hash_set { + private: + // Apparently identity is not stl-standard, so we define our own + struct Identity { + Value& operator()(Value& v) const { return v; } + const Value& operator()(const Value& v) const { return v; } + }; + + // The actual data + typedef sparse_hashtable<Value, Value, HashFcn, Identity, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::const_pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::const_reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::const_iterator iterator; + typedef typename ht::const_iterator const_iterator; + + + // Iterator functions -- recall all iterators are const + iterator begin() const { return rep.begin(); } + iterator end() const { return rep.end(); } + + + // Accessor functions + hasher hash_funct() const { return rep.hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit sparse_hash_set(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { } + + template <class InputIterator> + sparse_hash_set(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal()) + : rep(expected_max_items_in_table, hf, eql) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + void swap(sparse_hash_set& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + void resize(size_type hint) { rep.resize(hint); } + + void set_resizing_parameters(float shrink, float grow) { + return rep.set_resizing_parameters(shrink, grow); + } + + // Lookup routines + iterator find(const key_type& key) const { return rep.find(key); } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair<iterator, iterator> equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair<iterator, bool> insert(const value_type& obj) { + pair<typename ht::iterator, bool> p = rep.insert(obj); + return pair<iterator, bool>(p.first, p.second); // const to non-const + } + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted buckets. You can change the key as + // time goes on, or get rid of it entirely to be insert-only. + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +template <class Val, class HashFcn, class EqualKey, class Alloc> +inline void swap(sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1, + sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) { + hs1.swap(hs2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSE_HASH_SET_H_ */ diff --git a/3rdparty/google/sparsehash/densehashtable.h b/3rdparty/google/sparsehash/densehashtable.h new file mode 100644 index 0000000000..6ea1edc664 --- /dev/null +++ b/3rdparty/google/sparsehash/densehashtable.h @@ -0,0 +1,986 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A dense hashtable is a particular implementation of +// a hashtable: one that is meant to minimize memory allocation. +// It does this by using an array to store all the data. We +// steal a value from the key space to indicate "empty" array +// elements (ie indices where no item lives) and another to indicate +// "deleted" elements. +// +// (Note it is possible to change the value of the delete key +// on the fly; you can even remove it, though after that point +// the hashtable is insert_only until you set it again. The empty +// value however can't be changed.) +// +// To minimize allocation and pointer overhead, we use internal +// probing, in which the hashtable is a single table, and collisions +// are resolved by trying to insert again in another bucket. The +// most cache-efficient internal probing schemes are linear probing +// (which suffers, alas, from clumping) and quadratic probing, which +// is what we implement by default. +// +// Type requirements: value_type is required to be Copy Constructible +// and Default Constructible. It is not required to be (and commonly +// isn't) Assignable. +// +// You probably shouldn't use this code directly. Use +// <google/dense_hash_map> or <google/dense_hash_set> instead. + +// You can change the following below: +// HT_OCCUPANCY_FLT -- how full before we double size +// HT_EMPTY_FLT -- how empty before we halve size +// HT_MIN_BUCKETS -- default smallest bucket size +// +// You can also change enlarge_resize_percent (which defaults to +// HT_OCCUPANCY_FLT), and shrink_resize_percent (which defaults to +// HT_EMPTY_FLT) with set_resizing_parameters(). +// +// How to decide what values to use? +// shrink_resize_percent's default of .4 * OCCUPANCY_FLT, is probably good. +// HT_MIN_BUCKETS is probably unnecessary since you can specify +// (indirectly) the starting number of buckets at construct-time. +// For enlarge_resize_percent, you can use this chart to try to trade-off +// expected lookup time to the space taken up. By default, this +// code uses quadratic probing, though you can change it to linear +// via _JUMP below if you really want to. +// +// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html +// NUMBER OF PROBES / LOOKUP Successful Unsuccessful +// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) +// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 +// +// -- enlarge_resize_percent -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 +// QUADRATIC COLLISION RES. +// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 +// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 +// LINEAR COLLISION RES. +// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 +// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 + +#ifndef _DENSEHASHTABLE_H_ +#define _DENSEHASHTABLE_H_ + +// The probing method +// Linear probing +// #define JUMP_(key, num_probes) ( 1 ) +// Quadratic-ish probing +#define JUMP_(key, num_probes) ( num_probes ) + + +// Hashtable class, used to implement the hashed associative containers +// hash_set and hash_map. + +#include <google/sparsehash/sparseconfig.h> +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> // for abort() +#include <algorithm> // For swap(), eg +#include <iostream> // For cerr +#include <memory> // For uninitialized_fill, uninitialized_copy +#include <utility> // for pair<> +#include <iterator> // for facts about iterator tags +#include <google/type_traits.h> // for true_type, integral_constant, etc. + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template <class Value, class Key, class HashFcn, + class ExtractKey, class EqualKey, class Alloc> +class dense_hashtable; + +template <class V, class K, class HF, class ExK, class EqK, class A> +struct dense_hashtable_iterator; + +template <class V, class K, class HF, class ExK, class EqK, class A> +struct dense_hashtable_const_iterator; + +// We're just an array, but we need to skip over empty and deleted elements +template <class V, class K, class HF, class ExK, class EqK, class A> +struct dense_hashtable_iterator { + public: + typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator; + typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef V& reference; // Value + typedef V* pointer; + + // "Real" constructor and default constructor + dense_hashtable_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h, + pointer it, pointer it_end, bool advance) + : ht(h), pos(it), end(it_end) { + if (advance) advance_past_empty_and_deleted(); + } + dense_hashtable_iterator() { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on an empty or marked-deleted array element + void advance_past_empty_and_deleted() { + while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const dense_hashtable<V,K,HF,ExK,EqK,A> *ht; + pointer pos, end; +}; + + +// Now do it all again, but with const-ness! +template <class V, class K, class HF, class ExK, class EqK, class A> +struct dense_hashtable_const_iterator { + public: + typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator; + typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef const V& reference; // Value + typedef const V* pointer; + + // "Real" constructor and default constructor + dense_hashtable_const_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h, + pointer it, pointer it_end, bool advance) + : ht(h), pos(it), end(it_end) { + if (advance) advance_past_empty_and_deleted(); + } + dense_hashtable_const_iterator() { } + // This lets us convert regular iterators to const iterators + dense_hashtable_const_iterator(const iterator &it) + : ht(it.ht), pos(it.pos), end(it.end) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on an empty or marked-deleted array element + void advance_past_empty_and_deleted() { + while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) + ++pos; + } + const_iterator& operator++() { + assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; + } + const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const const_iterator& it) const { return pos == it.pos; } + bool operator!=(const const_iterator& it) const { return pos != it.pos; } + + + // The actual data + const dense_hashtable<V,K,HF,ExK,EqK,A> *ht; + pointer pos, end; +}; + +template <class Value, class Key, class HashFcn, + class ExtractKey, class EqualKey, class Alloc> +class dense_hashtable { + public: + typedef Key key_type; + typedef Value value_type; + typedef HashFcn hasher; + typedef EqualKey key_equal; + + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef dense_hashtable_iterator<Value, Key, HashFcn, + ExtractKey, EqualKey, Alloc> + iterator; + + typedef dense_hashtable_const_iterator<Value, Key, HashFcn, + ExtractKey, EqualKey, Alloc> + const_iterator; + + // How full we let the table get before we resize. Knuth says .8 is + // good -- higher causes us to probe too much, though saves memory + static const float HT_OCCUPANCY_FLT; // = 0.8; + + // How empty we let the table get before we resize lower. + // (0.0 means never resize lower.) + // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing + static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT + + // Minimum size we're willing to let hashtables be. + // Must be a power of two, and at least 4. + // Note, however, that for a given hashtable, the initial size is a + // function of the first constructor arg, and may be >HT_MIN_BUCKETS. + static const size_t HT_MIN_BUCKETS = 4; + + // By default, if you don't specify a hashtable size at + // construction-time, we use this size. Must be a power of two, and + // at least HT_MIN_BUCKETS. + static const size_t HT_DEFAULT_STARTING_BUCKETS = 32; + + + // ITERATOR FUNCTIONS + iterator begin() { return iterator(this, table, + table + num_buckets, true); } + iterator end() { return iterator(this, table + num_buckets, + table + num_buckets, true); } + const_iterator begin() const { return const_iterator(this, table, + table+num_buckets,true);} + const_iterator end() const { return const_iterator(this, table + num_buckets, + table+num_buckets,true);} + + // ACCESSOR FUNCTIONS for the things we templatize on, basically + hasher hash_funct() const { return hash; } + key_equal key_eq() const { return equals; } + + // Annoyingly, we can't copy values around, because they might have + // const components (they're probably pair<const X, Y>). We use + // explicit destructor invocation and placement new to get around + // this. Arg. + private: + void set_value(value_type* dst, const value_type& src) { + dst->~value_type(); + new(dst) value_type(src); + } + + void destroy_buckets(size_type first, size_type last) { + for ( ; first != last; ++first) + table[first].~value_type(); + } + + // DELETE HELPER FUNCTIONS + // This lets the user describe a key that will indicate deleted + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + private: + void squash_deleted() { // gets rid of any deleted entries we have + if ( num_deleted ) { // get rid of deleted before writing + dense_hashtable tmp(*this); // copying will get rid of deleted + swap(tmp); // now we are tmp + } + assert(num_deleted == 0); + } + + public: + void set_deleted_key(const value_type &val) { + // the empty indicator (if specified) and the deleted indicator + // must be different + assert(!use_empty || !equals(get_key(val), get_key(emptyval))); + // It's only safe to change what "deleted" means if we purge deleted guys + squash_deleted(); + use_deleted = true; + set_value(&delval, val); + } + void clear_deleted_key() { + squash_deleted(); + use_deleted = false; + } + + // These are public so the iterators can use them + // True if the item at position bucknum is "deleted" marker + bool test_deleted(size_type bucknum) const { + // The num_deleted test is crucial for read(): after read(), the ht values + // are garbage, and we don't want to think some of them are deleted. + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(table[bucknum]))); + } + bool test_deleted(const iterator &it) const { + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(*it))); + } + bool test_deleted(const const_iterator &it) const { + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(*it))); + } + // Set it so test_deleted is true. true if object didn't used to be deleted + // See below (at erase()) to explain why we allow const_iterators + bool set_deleted(const_iterator &it) { + assert(use_deleted); // bad if set_deleted_key() wasn't called + bool retval = !test_deleted(it); + // &* converts from iterator to value-type + set_value(const_cast<value_type*>(&(*it)), delval); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted + bool clear_deleted(const_iterator &it) { + assert(use_deleted); // bad if set_deleted_key() wasn't called + // happens automatically when we assign something else in its place + return test_deleted(it); + } + + // EMPTY HELPER FUNCTIONS + // This lets the user describe a key that will indicate empty (unused) + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + public: + // These are public so the iterators can use them + // True if the item at position bucknum is "empty" marker + bool test_empty(size_type bucknum) const { + assert(use_empty); // we always need to know what's empty! + return equals(get_key(emptyval), get_key(table[bucknum])); + } + bool test_empty(const iterator &it) const { + assert(use_empty); // we always need to know what's empty! + return equals(get_key(emptyval), get_key(*it)); + } + bool test_empty(const const_iterator &it) const { + assert(use_empty); // we always need to know what's empty! + return equals(get_key(emptyval), get_key(*it)); + } + + private: + // You can either set a range empty or an individual element + void set_empty(size_type bucknum) { + assert(use_empty); + set_value(&table[bucknum], emptyval); + } + void fill_range_with_empty(value_type* table_start, value_type* table_end) { + // Like set_empty(range), but doesn't destroy previous contents + STL_NAMESPACE::uninitialized_fill(table_start, table_end, emptyval); + } + void set_empty(size_type buckstart, size_type buckend) { + assert(use_empty); + destroy_buckets(buckstart, buckend); + fill_range_with_empty(table + buckstart, table + buckend); + } + + public: + // TODO(csilvers): change all callers of this to pass in a key instead, + // and take a const key_type instead of const value_type. + void set_empty_key(const value_type &val) { + // Once you set the empty key, you can't change it + assert(!use_empty); + // The deleted indicator (if specified) and the empty indicator + // must be different. + assert(!use_deleted || !equals(get_key(val), get_key(delval))); + use_empty = true; + set_value(&emptyval, val); + + assert(!table); // must set before first use + // num_buckets was set in constructor even though table was NULL + table = (value_type *) malloc(num_buckets * sizeof(*table)); + assert(table); + fill_range_with_empty(table, table + num_buckets); + } + + // FUNCTIONS CONCERNING SIZE + public: + size_type size() const { return num_elements - num_deleted; } + // Buckets are always a power of 2 + size_type max_size() const { return (size_type(-1) >> 1U) + 1; } + bool empty() const { return size() == 0; } + size_type bucket_count() const { return num_buckets; } + size_type max_bucket_count() const { return max_size(); } + size_type nonempty_bucket_count() const { return num_elements; } + + private: + // Because of the above, size_type(-1) is never legal; use it for errors + static const size_type ILLEGAL_BUCKET = size_type(-1); + + private: + // This is the smallest size a hashtable can be without being too crowded + // If you like, you can give a min #buckets as well as a min #elts + size_type min_size(size_type num_elts, size_type min_buckets_wanted) { + size_type sz = HT_MIN_BUCKETS; // min buckets allowed + while ( sz < min_buckets_wanted || num_elts >= sz * enlarge_resize_percent ) + sz *= 2; + return sz; + } + + // Used after a string of deletes + void maybe_shrink() { + assert(num_elements >= num_deleted); + assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two + assert(bucket_count() >= HT_MIN_BUCKETS); + + // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, + // we'll never shrink until you get relatively big, and we'll never + // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something + // like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will + // shrink us down to HT_MIN_BUCKETS buckets, which is too small. + if (shrink_threshold > 0 && + (num_elements-num_deleted) < shrink_threshold && + bucket_count() > HT_DEFAULT_STARTING_BUCKETS ) { + size_type sz = bucket_count() / 2; // find how much we should shrink + while ( sz > HT_DEFAULT_STARTING_BUCKETS && + (num_elements - num_deleted) < sz * shrink_resize_percent ) + sz /= 2; // stay a power of 2 + dense_hashtable tmp(*this, sz); // Do the actual resizing + swap(tmp); // now we are tmp + } + consider_shrink = false; // because we just considered it + } + + // We'll let you resize a hashtable -- though this makes us copy all! + // When you resize, you say, "make it big enough for this many more elements" + void resize_delta(size_type delta) { + if ( consider_shrink ) // see if lots of deletes happened + maybe_shrink(); + if ( bucket_count() > HT_MIN_BUCKETS && + (num_elements + delta) <= enlarge_threshold ) + return; // we're ok as we are + + // Sometimes, we need to resize just to get rid of all the + // "deleted" buckets that are clogging up the hashtable. So when + // deciding whether to resize, count the deleted buckets (which + // are currently taking up room). But later, when we decide what + // size to resize to, *don't* count deleted buckets, since they + // get discarded during the resize. + const size_type needed_size = min_size(num_elements + delta, 0); + if ( needed_size > bucket_count() ) { // we don't have enough buckets + const size_type resize_to = min_size(num_elements - num_deleted + delta, + 0); + dense_hashtable tmp(*this, resize_to); + swap(tmp); // now we are tmp + } + } + + // Increase number of buckets, assuming value_type has trivial copy + // constructor and destructor. (Really, we want it to have "trivial + // move", because that's what realloc does. But there's no way to + // capture that using type_traits, so we pretend that move(x, y) is + // equivalent to "x.~T(); new(x) T(y);" which is pretty much + // correct, if a bit conservative.) + void expand_array(size_t resize_to, true_type) { + table = (value_type *) realloc(table, resize_to * sizeof(value_type)); + assert(table); + fill_range_with_empty(table + num_buckets, table + resize_to); + } + + // Increase number of buckets, without special assumptions about value_type. + // TODO(austern): make this exception safe. Handle exceptions from + // value_type's copy constructor. + void expand_array(size_t resize_to, false_type) { + value_type* new_table = + (value_type *) malloc(resize_to * sizeof(value_type)); + assert(new_table); + STL_NAMESPACE::uninitialized_copy(table, table + num_buckets, new_table); + fill_range_with_empty(new_table + num_buckets, new_table + resize_to); + destroy_buckets(0, num_buckets); + free(table); + table = new_table; + } + + // Used to actually do the rehashing when we grow/shrink a hashtable + void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + const size_type resize_to = min_size(ht.size(), min_buckets_wanted); + if ( resize_to > bucket_count() ) { // we don't have enough buckets + typedef integral_constant<bool, + (has_trivial_copy<value_type>::value && + has_trivial_destructor<value_type>::value)> + realloc_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)" + expand_array(resize_to, realloc_ok()); + num_buckets = resize_to; + reset_thresholds(); + } + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two + for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + const size_type bucket_count_minus_one = bucket_count() - 1; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + !test_empty(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { + ++num_probes; + assert(num_probes < bucket_count()); // or else the hashtable is full + } + set_value(&table[bucknum], *it); // copies the value to here + num_elements++; + } + } + + // Required by the spec for hashed associative container + public: + // Though the docs say this should be num_buckets, I think it's much + // more useful as req_elements. As a special feature, calling with + // req_elements==0 will cause us to shrink if we can, saving space. + void resize(size_type req_elements) { // resize to this or larger + if ( consider_shrink || req_elements == 0 ) + maybe_shrink(); + if ( req_elements > num_elements ) + return resize_delta(req_elements - num_elements); + } + + // Change the value of shrink_resize_percent and + // enlarge_resize_percent. The description at the beginning of this + // file explains how to choose the values. Setting the shrink + // parameter to 0.0 ensures that the table never shrinks. + void set_resizing_parameters(float shrink, float grow) { + assert(shrink >= 0.0); + assert(grow <= 1.0); + assert(shrink <= grow/2.0); + shrink_resize_percent = shrink; + enlarge_resize_percent = grow; + reset_thresholds(); + } + + // CONSTRUCTORS -- as required by the specs, we take a size, + // but also let you specify a hashfunction, key comparator, + // and key extractor. We also define a copy constructor and =. + // DESTRUCTOR -- needs to free the table + explicit dense_hashtable(size_type expected_max_items_in_table = 0, + const HashFcn& hf = HashFcn(), + const EqualKey& eql = EqualKey(), + const ExtractKey& ext = ExtractKey()) + : hash(hf), equals(eql), get_key(ext), num_deleted(0), + use_deleted(false), use_empty(false), + delval(), emptyval(), enlarge_resize_percent(HT_OCCUPANCY_FLT), + shrink_resize_percent(HT_EMPTY_FLT), table(NULL), + num_buckets(expected_max_items_in_table == 0 + ? HT_DEFAULT_STARTING_BUCKETS + : min_size(expected_max_items_in_table, 0)), + num_elements(0) { + // table is NULL until emptyval is set. However, we set num_buckets + // here so we know how much space to allocate once emptyval is set + reset_thresholds(); + } + + // As a convenience for resize(), we allow an optional second argument + // which lets you make this new hashtable a different size than ht + dense_hashtable(const dense_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_deleted(0), + use_deleted(ht.use_deleted), use_empty(ht.use_empty), + delval(ht.delval), emptyval(ht.emptyval), + enlarge_resize_percent(ht.enlarge_resize_percent), + shrink_resize_percent(ht.shrink_resize_percent), table(NULL), + num_buckets(0), num_elements(0) { + reset_thresholds(); + copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries + } + + dense_hashtable& operator= (const dense_hashtable& ht) { + if (&ht == this) return *this; // don't copy onto ourselves + clear(); + hash = ht.hash; + equals = ht.equals; + get_key = ht.get_key; + use_deleted = ht.use_deleted; + use_empty = ht.use_empty; + set_value(&delval, ht.delval); + set_value(&emptyval, ht.emptyval); + enlarge_resize_percent = ht.enlarge_resize_percent; + shrink_resize_percent = ht.shrink_resize_percent; + copy_from(ht, HT_MIN_BUCKETS); // sets num_deleted to 0 too + return *this; + } + + ~dense_hashtable() { + if (table) { + destroy_buckets(0, num_buckets); + free(table); + } + } + + // Many STL algorithms use swap instead of copy constructors + void swap(dense_hashtable& ht) { + STL_NAMESPACE::swap(hash, ht.hash); + STL_NAMESPACE::swap(equals, ht.equals); + STL_NAMESPACE::swap(get_key, ht.get_key); + STL_NAMESPACE::swap(num_deleted, ht.num_deleted); + STL_NAMESPACE::swap(use_deleted, ht.use_deleted); + STL_NAMESPACE::swap(use_empty, ht.use_empty); + STL_NAMESPACE::swap(enlarge_resize_percent, ht.enlarge_resize_percent); + STL_NAMESPACE::swap(shrink_resize_percent, ht.shrink_resize_percent); + { value_type tmp; // for annoying reasons, swap() doesn't work + set_value(&tmp, delval); + set_value(&delval, ht.delval); + set_value(&ht.delval, tmp); + } + { value_type tmp; // for annoying reasons, swap() doesn't work + set_value(&tmp, emptyval); + set_value(&emptyval, ht.emptyval); + set_value(&ht.emptyval, tmp); + } + STL_NAMESPACE::swap(table, ht.table); + STL_NAMESPACE::swap(num_buckets, ht.num_buckets); + STL_NAMESPACE::swap(num_elements, ht.num_elements); + reset_thresholds(); + ht.reset_thresholds(); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + if (table) + destroy_buckets(0, num_buckets); + num_buckets = min_size(0,0); // our new size + reset_thresholds(); + table = (value_type *) realloc(table, num_buckets * sizeof(*table)); + assert(table); + fill_range_with_empty(table, table + num_buckets); + num_elements = 0; + num_deleted = 0; + } + + // Clear the table without resizing it. + // Mimicks the stl_hashtable's behaviour when clear()-ing in that it + // does not modify the bucket count + void clear_no_resize() { + if (table) { + set_empty(0, num_buckets); + } + // don't consider to shrink before another erase() + reset_thresholds(); + num_elements = 0; + num_deleted = 0; + } + + // LOOKUP ROUTINES + private: + // Returns a pair of positions: 1st where the object is, 2nd where + // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET + // if object is not found; 2nd is ILLEGAL_BUCKET if it is. + // Note: because of deletions where-to-insert is not trivial: it's the + // first deleted bucket we see, as long as we don't find the key later + pair<size_type, size_type> find_position(const key_type &key) const { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + size_type insert_pos = ILLEGAL_BUCKET; // where we would insert + while ( 1 ) { // probe until something happens + if ( test_empty(bucknum) ) { // bucket is empty + if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert + return pair<size_type,size_type>(ILLEGAL_BUCKET, bucknum); + else + return pair<size_type,size_type>(ILLEGAL_BUCKET, insert_pos); + + } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert + if ( insert_pos == ILLEGAL_BUCKET ) + insert_pos = bucknum; + + } else if ( equals(key, get_key(table[bucknum])) ) { + return pair<size_type,size_type>(bucknum, ILLEGAL_BUCKET); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count()); // don't probe too many times! + } + } + + public: + iterator find(const key_type& key) { + if ( size() == 0 ) return end(); + pair<size_type, size_type> pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return iterator(this, table + pos.first, table + num_buckets, false); + } + + const_iterator find(const key_type& key) const { + if ( size() == 0 ) return end(); + pair<size_type, size_type> pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return const_iterator(this, table + pos.first, table+num_buckets, false); + } + + // Counts how many elements have key key. For maps, it's either 0 or 1. + size_type count(const key_type &key) const { + pair<size_type, size_type> pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? 0 : 1; + } + + // Likewise, equal_range doesn't really make sense for us. Oh well. + pair<iterator,iterator> equal_range(const key_type& key) { + const iterator pos = find(key); // either an iterator or end + return pair<iterator,iterator>(pos, pos); + } + pair<const_iterator,const_iterator> equal_range(const key_type& key) const { + const const_iterator pos = find(key); // either an iterator or end + return pair<iterator,iterator>(pos, pos); + } + + + // INSERTION ROUTINES + private: + // If you know *this is big enough to hold obj, use this routine + pair<iterator, bool> insert_noresize(const value_type& obj) { + // First, double-check we're not inserting delval or emptyval + assert(!use_empty || !equals(get_key(obj), get_key(emptyval))); + assert(!use_deleted || !equals(get_key(obj), get_key(delval))); + const pair<size_type,size_type> pos = find_position(get_key(obj)); + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return pair<iterator,bool>(iterator(this, table + pos.first, + table + num_buckets, false), + false); // false: we didn't insert + } else { // pos.second says where to put it + if ( test_deleted(pos.second) ) { // just replace if it's been del. + const_iterator delpos(this, table + pos.second, // shrug: + table + num_buckets, false);// shouldn't need const + clear_deleted(delpos); + assert( num_deleted > 0); + --num_deleted; // used to be, now it isn't + } else { + ++num_elements; // replacing an empty bucket + } + set_value(&table[pos.second], obj); + return pair<iterator,bool>(iterator(this, table + pos.second, + table + num_buckets, false), + true); // true: we did insert + } + } + + public: + // This is the normal insert routine, used by the outside world + pair<iterator, bool> insert(const value_type& obj) { + resize_delta(1); // adding an object, grow if need be + return insert_noresize(obj); + } + + // When inserting a lot at a time, we specialize on the type of iterator + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { + // specializes on iterator type + insert(f, l, typename STL_NAMESPACE::iterator_traits<InputIterator>::iterator_category()); + } + + // Iterator supports operator-, resize before inserting + template <class ForwardIterator> + void insert(ForwardIterator f, ForwardIterator l, + STL_NAMESPACE::forward_iterator_tag) { + size_type n = STL_NAMESPACE::distance(f, l); // TODO(csilvers): standard? + resize_delta(n); + for ( ; n > 0; --n, ++f) + insert_noresize(*f); + } + + // Arbitrary iterator, can't tell how much to resize + template <class InputIterator> + void insert(InputIterator f, InputIterator l, + STL_NAMESPACE::input_iterator_tag) { + for ( ; f != l; ++f) + insert(*f); + } + + + // DELETION ROUTINES + size_type erase(const key_type& key) { + // First, double-check we're not trying to erase delval or emptyval + assert(!use_empty || !equals(key, get_key(emptyval))); + assert(!use_deleted || !equals(key, get_key(delval))); + const_iterator pos = find(key); // shrug: shouldn't need to be const + if ( pos != end() ) { + assert(!test_deleted(pos)); // or find() shouldn't have returned it + set_deleted(pos); + ++num_deleted; + consider_shrink = true; // will think about shrink after next insert + return 1; // because we deleted one thing + } else { + return 0; // because we deleted nothing + } + } + + // This is really evil: really it should be iterator, not const_iterator. + // But...the only reason keys are const is to allow lookup. + // Since that's a moot issue for deleted keys, we allow const_iterators + void erase(const_iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + consider_shrink = true; // will think about shrink after next insert + } + } + + void erase(const_iterator f, const_iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + consider_shrink = true; // will think about shrink after next insert + } + + + // COMPARISON + bool operator==(const dense_hashtable& ht) const { + if (size() != ht.size()) { + return false; + } else if (this == &ht) { + return true; + } else { + // Iterate through the elements in "this" and see if the + // corresponding element is in ht + for ( const_iterator it = begin(); it != end(); ++it ) { + const_iterator it2 = ht.find(get_key(*it)); + if ((it2 == ht.end()) || (*it != *it2)) { + return false; + } + } + return true; + } + } + bool operator!=(const dense_hashtable& ht) const { + return !(*this == ht); + } + + + // I/O + // We support reading and writing hashtables to disk. Alas, since + // I don't know how to write a hasher or key_equal, you have to make + // sure everything but the table is the same. We compact before writing + // + // NOTE: These functions are currently TODO. They've not been implemented. + bool write_metadata(FILE *fp) { + squash_deleted(); // so we don't have to worry about delval + return false; // TODO + } + + bool read_metadata(FILE *fp) { + num_deleted = 0; // since we got rid before writing + assert(use_empty); // have to set this before calling us + if (table) free(table); // we'll make our own + // TODO: read magic number + // TODO: read num_buckets + reset_thresholds(); + table = (value_type *) malloc(num_buckets * sizeof(*table)); + assert(table); + fill_range_with_empty(table, table + num_buckets); + // TODO: read num_elements + for ( size_type i = 0; i < num_elements; ++i ) { + // TODO: read bucket_num + // TODO: set with non-empty, non-deleted value + } + return false; // TODO + } + + // If your keys and values are simple enough, we can write them to + // disk for you. "simple enough" means value_type is a POD type + // that contains no pointers. However, we don't try to normalize + // endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_iterator it = begin(); it != end(); ++it ) { + // TODO: skip empty/deleted values + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return false; + } + + // When reading, we have to override the potential const-ness of *it + bool read_nopointer_data(FILE *fp) { + for ( iterator it = begin(); it != end(); ++it ) { + // TODO: skip empty/deleted values + if ( !fread(reinterpret_cast<void*>(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return false; + } + + private: + // The actual data + hasher hash; // required by hashed_associative_container + key_equal equals; + ExtractKey get_key; + size_type num_deleted; // how many occupied buckets are marked deleted + bool use_deleted; // false until delval has been set + bool use_empty; // you must do this before you start + value_type delval; // which key marks deleted entries + value_type emptyval; // which key marks unused entries + float enlarge_resize_percent; // how full before resize + float shrink_resize_percent; // how empty before resize + size_type shrink_threshold; // num_buckets * shrink_resize_percent + size_type enlarge_threshold; // num_buckets * enlarge_resize_percent + value_type *table; + size_type num_buckets; + size_type num_elements; + bool consider_shrink; // true if we should try to shrink before next insert + + void reset_thresholds() { + enlarge_threshold = static_cast<size_type>(num_buckets + * enlarge_resize_percent); + shrink_threshold = static_cast<size_type>(num_buckets + * shrink_resize_percent); + consider_shrink = false; // whatever caused us to reset already considered + } +}; + +// We need a global swap as well +template <class V, class K, class HF, class ExK, class EqK, class A> +inline void swap(dense_hashtable<V,K,HF,ExK,EqK,A> &x, + dense_hashtable<V,K,HF,ExK,EqK,A> &y) { + x.swap(y); +} + +#undef JUMP_ + +template <class V, class K, class HF, class ExK, class EqK, class A> +const typename dense_hashtable<V,K,HF,ExK,EqK,A>::size_type + dense_hashtable<V,K,HF,ExK,EqK,A>::ILLEGAL_BUCKET; + +// How full we let the table get before we resize. Knuth says .8 is +// good -- higher causes us to probe too much, though saves memory +template <class V, class K, class HF, class ExK, class EqK, class A> +const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.5f; + +// How empty we let the table get before we resize lower. +// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing +template <class V, class K, class HF, class ExK, class EqK, class A> +const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4f * +dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT; + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSEHASHTABLE_H_ */ diff --git a/3rdparty/google/sparsehash/sparseconfig.h b/3rdparty/google/sparsehash/sparseconfig.h new file mode 100644 index 0000000000..0ae597ed5b --- /dev/null +++ b/3rdparty/google/sparsehash/sparseconfig.h @@ -0,0 +1,74 @@ +#ifndef SPARSEHASH_SPARSECONFIG_H__ +#define SPARSEHASH_SPARSECONFIG_H__ + +// [AIR] : I couldn't make the google "windows" folder concept work. +// This does, and we only care of GCC and MSVC right now anyway. + +#if defined( _MSC_VER ) + + #define GOOGLE_NAMESPACE google + #define HASH_NAMESPACE stdext + #define HASH_FUN_H <hash_map> + #define SPARSEHASH_HASH HASH_NAMESPACE::hash_compare + #undef HAVE_UINT16_T + #undef HAVE_U_INT16_T + #define HAVE___UINT16 1 + #define HAVE_LONG_LONG 1 + #define HAVE_SYS_TYPES_H 1 + #undef HAVE_STDINT_H + #undef HAVE_INTTYPES_H + #define HAVE_MEMCPY 1 + #define STL_NAMESPACE std + #define _END_GOOGLE_NAMESPACE_ } + #define _START_GOOGLE_NAMESPACE_ namespace GOOGLE_NAMESPACE { + +#else //if defined( GNUC ) + + /* Namespace for Google classes */ + #define GOOGLE_NAMESPACE google + + /* the location of <hash_fun.h>/<stl_hash_fun.h> */ + #define HASH_FUN_H <ext/hash_fun.h> + + /* the namespace of hash_map/hash_set */ + #define HASH_NAMESPACE __gnu_cxx + + /* Define to 1 if you have the <inttypes.h> header file. */ + #define HAVE_INTTYPES_H 1 + + /* Define to 1 if the system has the type `long long'. */ + #define HAVE_LONG_LONG 1 + + /* Define to 1 if you have the `memcpy' function. */ + #define HAVE_MEMCPY 1 + + /* Define to 1 if you have the <stdint.h> header file. */ + #define HAVE_STDINT_H 1 + + /* Define to 1 if you have the <sys/types.h> header file. */ + #define HAVE_SYS_TYPES_H 1 + + /* Define to 1 if the system has the type `uint16_t'. */ + #define HAVE_UINT16_T 1 + + /* Define to 1 if the system has the type `u_int16_t'. */ + #define HAVE_U_INT16_T 1 + + /* Define to 1 if the system has the type `__uint16'. */ + /* #undef HAVE___UINT16 */ + + /* The system-provided hash function including the namespace. */ + #define SPARSEHASH_HASH HASH_NAMESPACE::hash + + /* the namespace where STL code like vector<> is defined */ + #define STL_NAMESPACE std + + /* Stops putting the code inside the Google namespace */ + #define _END_GOOGLE_NAMESPACE_ } + + /* Puts following code inside the Google namespace */ + #define _START_GOOGLE_NAMESPACE_ namespace google { + +#endif + +#endif diff --git a/3rdparty/google/sparsehash/sparsehashtable.h b/3rdparty/google/sparsehash/sparsehashtable.h new file mode 100644 index 0000000000..28e8ba7d4f --- /dev/null +++ b/3rdparty/google/sparsehash/sparsehashtable.h @@ -0,0 +1,941 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A sparse hashtable is a particular implementation of +// a hashtable: one that is meant to minimize memory use. +// It does this by using a *sparse table* (cf sparsetable.h), +// which uses between 1 and 2 bits to store empty buckets +// (we may need another bit for hashtables that support deletion). +// +// When empty buckets are so cheap, an appealing hashtable +// implementation is internal probing, in which the hashtable +// is a single table, and collisions are resolved by trying +// to insert again in another bucket. The most cache-efficient +// internal probing schemes are linear probing (which suffers, +// alas, from clumping) and quadratic probing, which is what +// we implement by default. +// +// Deleted buckets are a bit of a pain. We have to somehow mark +// deleted buckets (the probing must distinguish them from empty +// buckets). The most principled way is to have another bitmap, +// but that's annoying and takes up space. Instead we let the +// user specify an "impossible" key. We set deleted buckets +// to have the impossible key. +// +// Note it is possible to change the value of the delete key +// on the fly; you can even remove it, though after that point +// the hashtable is insert_only until you set it again. +// +// You probably shouldn't use this code directly. Use +// <google/sparse_hash_table> or <google/sparse_hash_set> instead. +// +// You can modify the following, below: +// HT_OCCUPANCY_FLT -- how full before we double size +// HT_EMPTY_FLT -- how empty before we halve size +// HT_MIN_BUCKETS -- smallest bucket size +// HT_DEFAULT_STARTING_BUCKETS -- default bucket size at construct-time +// +// You can also change enlarge_resize_percent (which defaults to +// HT_OCCUPANCY_FLT), and shrink_resize_percent (which defaults to +// HT_EMPTY_FLT) with set_resizing_parameters(). +// +// How to decide what values to use? +// shrink_resize_percent's default of .4 * OCCUPANCY_FLT, is probably good. +// HT_MIN_BUCKETS is probably unnecessary since you can specify +// (indirectly) the starting number of buckets at construct-time. +// For enlarge_resize_percent, you can use this chart to try to trade-off +// expected lookup time to the space taken up. By default, this +// code uses quadratic probing, though you can change it to linear +// via _JUMP below if you really want to. +// +// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html +// NUMBER OF PROBES / LOOKUP Successful Unsuccessful +// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) +// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 +// +// -- enlarge_resize_percent -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 +// QUADRATIC COLLISION RES. +// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 +// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 +// LINEAR COLLISION RES. +// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 +// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 +// +// The value type is required to be copy constructible and default +// constructible, but it need not be (and commonly isn't) assignable. + +#ifndef _SPARSEHASHTABLE_H_ +#define _SPARSEHASHTABLE_H_ + +#ifndef SPARSEHASH_STAT_UPDATE +#define SPARSEHASH_STAT_UPDATE(x) ((void) 0) +#endif + +// The probing method +// Linear probing +// #define JUMP_(key, num_probes) ( 1 ) +// Quadratic-ish probing +#define JUMP_(key, num_probes) ( num_probes ) + + +// Hashtable class, used to implement the hashed associative containers +// hash_set and hash_map. + +#include <google/sparsehash/sparseconfig.h> +#include <assert.h> +#include <algorithm> // For swap(), eg +#include <iterator> // for facts about iterator tags +#include <utility> // for pair<> +#include <google/sparsetable> // Since that's basically what we are + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +// Alloc is completely ignored. It is present as a template parameter only +// for the sake of being compatible with the old SGI hashtable interface. +// TODO(csilvers): is that the right thing to do? + +template <class Value, class Key, class HashFcn, + class ExtractKey, class EqualKey, class Alloc> +class sparse_hashtable; + +template <class V, class K, class HF, class ExK, class EqK, class A> +struct sparse_hashtable_iterator; + +template <class V, class K, class HF, class ExK, class EqK, class A> +struct sparse_hashtable_const_iterator; + +// As far as iterating, we're basically just a sparsetable +// that skips over deleted elements. +template <class V, class K, class HF, class ExK, class EqK, class A> +struct sparse_hashtable_iterator { + public: + typedef sparse_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator; + typedef sparse_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator; + typedef typename sparsetable<V>::nonempty_iterator st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef V& reference; // Value + typedef V* pointer; + + // "Real" constructor and default constructor + sparse_hashtable_iterator(const sparse_hashtable<V,K,HF,ExK,EqK,A> *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + sparse_hashtable_iterator() { } // not ever used internally + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht; + st_iterator pos, end; +}; + +// Now do it all again, but with const-ness! +template <class V, class K, class HF, class ExK, class EqK, class A> +struct sparse_hashtable_const_iterator { + public: + typedef sparse_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator; + typedef sparse_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator; + typedef typename sparsetable<V>::const_nonempty_iterator st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef const V& reference; // Value + typedef const V* pointer; + + // "Real" constructor and default constructor + sparse_hashtable_const_iterator(const sparse_hashtable<V,K,HF,ExK,EqK,A> *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + // This lets us convert regular iterators to const iterators + sparse_hashtable_const_iterator() { } // never used internally + sparse_hashtable_const_iterator(const iterator &it) + : ht(it.ht), pos(it.pos), end(it.end) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + const_iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const const_iterator& it) const { return pos == it.pos; } + bool operator!=(const const_iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht; + st_iterator pos, end; +}; + +// And once again, but this time freeing up memory as we iterate +template <class V, class K, class HF, class ExK, class EqK, class A> +struct sparse_hashtable_destructive_iterator { + public: + typedef sparse_hashtable_destructive_iterator<V,K,HF,ExK,EqK,A> iterator; + typedef typename sparsetable<V>::destructive_iterator st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef V& reference; // Value + typedef V* pointer; + + // "Real" constructor and default constructor + sparse_hashtable_destructive_iterator(const + sparse_hashtable<V,K,HF,ExK,EqK,A> *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + sparse_hashtable_destructive_iterator() { } // never used internally + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht; + st_iterator pos, end; +}; + + +template <class Value, class Key, class HashFcn, + class ExtractKey, class EqualKey, class Alloc> +class sparse_hashtable { + public: + typedef Key key_type; + typedef Value value_type; + typedef HashFcn hasher; + typedef EqualKey key_equal; + + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef sparse_hashtable_iterator<Value, Key, HashFcn, + ExtractKey, EqualKey, Alloc> + iterator; + + typedef sparse_hashtable_const_iterator<Value, Key, HashFcn, + ExtractKey, EqualKey, Alloc> + const_iterator; + + typedef sparse_hashtable_destructive_iterator<Value, Key, HashFcn, + ExtractKey, EqualKey, Alloc> + destructive_iterator; + + + // How full we let the table get before we resize. Knuth says .8 is + // good -- higher causes us to probe too much, though saves memory + static const float HT_OCCUPANCY_FLT; // = 0.8f; + + // How empty we let the table get before we resize lower. + // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing + static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT; + + // Minimum size we're willing to let hashtables be. + // Must be a power of two, and at least 4. + // Note, however, that for a given hashtable, the minimum size is + // determined by the first constructor arg, and may be >HT_MIN_BUCKETS. + static const size_t HT_MIN_BUCKETS = 4; + + // By default, if you don't specify a hashtable size at + // construction-time, we use this size. Must be a power of two, and + // at least HT_MIN_BUCKETS. + static const size_t HT_DEFAULT_STARTING_BUCKETS = 32; + + // ITERATOR FUNCTIONS + iterator begin() { return iterator(this, table.nonempty_begin(), + table.nonempty_end()); } + iterator end() { return iterator(this, table.nonempty_end(), + table.nonempty_end()); } + const_iterator begin() const { return const_iterator(this, + table.nonempty_begin(), + table.nonempty_end()); } + const_iterator end() const { return const_iterator(this, + table.nonempty_end(), + table.nonempty_end()); } + + // This is used when resizing + destructive_iterator destructive_begin() { + return destructive_iterator(this, table.destructive_begin(), + table.destructive_end()); + } + destructive_iterator destructive_end() { + return destructive_iterator(this, table.destructive_end(), + table.destructive_end()); + } + + + // ACCESSOR FUNCTIONS for the things we templatize on, basically + hasher hash_funct() const { return hash; } + key_equal key_eq() const { return equals; } + + // We need to copy values when we set the special marker for deleted + // elements, but, annoyingly, we can't just use the copy assignment + // operator because value_type might not be assignable (it's often + // pair<const X, Y>). We use explicit destructor invocation and + // placement new to get around this. Arg. + private: + void set_value(value_type* dst, const value_type src) { + dst->~value_type(); // delete the old value, if any + new(dst) value_type(src); + } + + // This is used as a tag for the copy constructor, saying to destroy its + // arg We have two ways of destructively copying: with potentially growing + // the hashtable as we copy, and without. To make sure the outside world + // can't do a destructive copy, we make the typename private. + enum MoveDontCopyT {MoveDontCopy, MoveDontGrow}; + + + // DELETE HELPER FUNCTIONS + // This lets the user describe a key that will indicate deleted + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + private: + void squash_deleted() { // gets rid of any deleted entries we have + if ( num_deleted ) { // get rid of deleted before writing + sparse_hashtable tmp(MoveDontGrow, *this); + swap(tmp); // now we are tmp + } + assert(num_deleted == 0); + } + + public: + void set_deleted_key(const value_type &val) { + // It's only safe to change what "deleted" means if we purge deleted guys + squash_deleted(); + use_deleted = true; + set_value(&delval, val); // save the key (and rest of val too) + } + void clear_deleted_key() { + squash_deleted(); + use_deleted = false; + } + + // These are public so the iterators can use them + // True if the item at position bucknum is "deleted" marker + bool test_deleted(size_type bucknum) const { + // The num_deleted test is crucial for read(): after read(), the ht values + // are garbage, and we don't want to think some of them are deleted. + return (use_deleted && num_deleted > 0 && table.test(bucknum) && + equals(get_key(delval), get_key(table.get(bucknum)))); + } + bool test_deleted(const iterator &it) const { + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(*it))); + } + bool test_deleted(const const_iterator &it) const { + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(*it))); + } + bool test_deleted(const destructive_iterator &it) const { + return (use_deleted && num_deleted > 0 && + equals(get_key(delval), get_key(*it))); + } + // Set it so test_deleted is true. true if object didn't used to be deleted + // See below (at erase()) to explain why we allow const_iterators + bool set_deleted(const_iterator &it) { + assert(use_deleted); // bad if set_deleted_key() wasn't called + bool retval = !test_deleted(it); + // &* converts from iterator to value-type + set_value(const_cast<value_type*>(&(*it)), delval); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted + bool clear_deleted(const_iterator &it) { + assert(use_deleted); // bad if set_deleted_key() wasn't called + // happens automatically when we assign something else in its place + return test_deleted(it); + } + + + // FUNCTIONS CONCERNING SIZE + size_type size() const { return table.num_nonempty() - num_deleted; } + // Buckets are always a power of 2 + size_type max_size() const { return (size_type(-1) >> 1U) + 1; } + bool empty() const { return size() == 0; } + size_type bucket_count() const { return table.size(); } + size_type max_bucket_count() const { return max_size(); } + + private: + // Because of the above, size_type(-1) is never legal; use it for errors + static const size_type ILLEGAL_BUCKET = size_type(-1); + + private: + // This is the smallest size a hashtable can be without being too crowded + // If you like, you can give a min #buckets as well as a min #elts + size_type min_size(size_type num_elts, size_type min_buckets_wanted) { + size_type sz = HT_MIN_BUCKETS; + while ( sz < min_buckets_wanted || num_elts >= sz * enlarge_resize_percent ) + sz *= 2; + return sz; + } + + // Used after a string of deletes + void maybe_shrink() { + assert(table.num_nonempty() >= num_deleted); + assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two + assert(bucket_count() >= HT_MIN_BUCKETS); + + // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, + // we'll never shrink until you get relatively big, and we'll never + // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something + // like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will + // shrink us down to HT_MIN_BUCKETS buckets, which is too small. + if (shrink_threshold > 0 + && (table.num_nonempty()-num_deleted) < shrink_threshold && + bucket_count() > HT_DEFAULT_STARTING_BUCKETS ) { + size_type sz = bucket_count() / 2; // find how much we should shrink + while ( sz > HT_DEFAULT_STARTING_BUCKETS && + (table.num_nonempty() - num_deleted) <= sz * + shrink_resize_percent ) + sz /= 2; // stay a power of 2 + sparse_hashtable tmp(MoveDontCopy, *this, sz); + swap(tmp); // now we are tmp + } + consider_shrink = false; // because we just considered it + } + + // We'll let you resize a hashtable -- though this makes us copy all! + // When you resize, you say, "make it big enough for this many more elements" + void resize_delta(size_type delta) { + if ( consider_shrink ) // see if lots of deletes happened + maybe_shrink(); + if ( bucket_count() >= HT_MIN_BUCKETS && + (table.num_nonempty() + delta) <= enlarge_threshold ) + return; // we're ok as we are + + // Sometimes, we need to resize just to get rid of all the + // "deleted" buckets that are clogging up the hashtable. So when + // deciding whether to resize, count the deleted buckets (which + // are currently taking up room). But later, when we decide what + // size to resize to, *don't* count deleted buckets, since they + // get discarded during the resize. + const size_type needed_size = min_size(table.num_nonempty() + delta, 0); + if ( needed_size > bucket_count() ) { // we don't have enough buckets + const size_type resize_to = min_size(table.num_nonempty() - num_deleted + + delta, 0); + sparse_hashtable tmp(MoveDontCopy, *this, resize_to); + swap(tmp); // now we are tmp + } + } + + // Used to actually do the rehashing when we grow/shrink a hashtable + void copy_from(const sparse_hashtable &ht, size_type min_buckets_wanted) { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + const size_type resize_to = min_size(ht.size(), min_buckets_wanted); + if ( resize_to > bucket_count() ) { // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + reset_thresholds(); + } + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two + for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + const size_type bucket_count_minus_one = bucket_count() - 1; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + table.test(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { + ++num_probes; + assert(num_probes < bucket_count()); // or else the hashtable is full + } + table.set(bucknum, *it); // copies the value to here + } + } + + // Implementation is like copy_from, but it destroys the table of the + // "from" guy by freeing sparsetable memory as we iterate. This is + // useful in resizing, since we're throwing away the "from" guy anyway. + void move_from(MoveDontCopyT mover, sparse_hashtable &ht, + size_type min_buckets_wanted) { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + size_t resize_to; + if ( mover == MoveDontGrow ) + resize_to = ht.bucket_count(); // keep same size as old ht + else // MoveDontCopy + resize_to = min_size(ht.size(), min_buckets_wanted); + if ( resize_to > bucket_count() ) { // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + reset_thresholds(); + } + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two + // THIS IS THE MAJOR LINE THAT DIFFERS FROM COPY_FROM(): + for ( destructive_iterator it = ht.destructive_begin(); + it != ht.destructive_end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + for ( bucknum = hash(get_key(*it)) & (bucket_count()-1); // h % buck_cnt + table.test(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & (bucket_count()-1) ) { + ++num_probes; + assert(num_probes < bucket_count()); // or else the hashtable is full + } + table.set(bucknum, *it); // copies the value to here + } + } + + + // Required by the spec for hashed associative container + public: + // Though the docs say this should be num_buckets, I think it's much + // more useful as num_elements. As a special feature, calling with + // req_elements==0 will cause us to shrink if we can, saving space. + void resize(size_type req_elements) { // resize to this or larger + if ( consider_shrink || req_elements == 0 ) + maybe_shrink(); + if ( req_elements > table.num_nonempty() ) // we only grow + resize_delta(req_elements - table.num_nonempty()); + } + + // Change the value of shrink_resize_percent and + // enlarge_resize_percent. The description at the beginning of this + // file explains how to choose the values. Setting the shrink + // parameter to 0.0 ensures that the table never shrinks. + void set_resizing_parameters(float shrink, float grow) { + assert(shrink >= 0.0); + assert(grow <= 1.0); + assert(shrink <= grow/2.0); + shrink_resize_percent = shrink; + enlarge_resize_percent = grow; + reset_thresholds(); + } + + // CONSTRUCTORS -- as required by the specs, we take a size, + // but also let you specify a hashfunction, key comparator, + // and key extractor. We also define a copy constructor and =. + // DESTRUCTOR -- the default is fine, surprisingly. + explicit sparse_hashtable(size_type expected_max_items_in_table = 0, + const HashFcn& hf = HashFcn(), + const EqualKey& eql = EqualKey(), + const ExtractKey& ext = ExtractKey()) + : hash(hf), equals(eql), get_key(ext), num_deleted(0), use_deleted(false), + delval(), enlarge_resize_percent(HT_OCCUPANCY_FLT), + shrink_resize_percent(HT_EMPTY_FLT), + table(expected_max_items_in_table == 0 + ? HT_DEFAULT_STARTING_BUCKETS + : min_size(expected_max_items_in_table, 0)) { + reset_thresholds(); + } + + // As a convenience for resize(), we allow an optional second argument + // which lets you make this new hashtable a different size than ht. + // We also provide a mechanism of saying you want to "move" the ht argument + // into us instead of copying. + sparse_hashtable(const sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), + num_deleted(0), use_deleted(ht.use_deleted), delval(ht.delval), + enlarge_resize_percent(ht.enlarge_resize_percent), + shrink_resize_percent(ht.shrink_resize_percent), + table() { + reset_thresholds(); + copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries + } + sparse_hashtable(MoveDontCopyT mover, sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), + num_deleted(0), use_deleted(ht.use_deleted), delval(ht.delval), + enlarge_resize_percent(ht.enlarge_resize_percent), + shrink_resize_percent(ht.shrink_resize_percent), + table() { + reset_thresholds(); + move_from(mover, ht, min_buckets_wanted); // ignores deleted entries + } + + sparse_hashtable& operator= (const sparse_hashtable& ht) { + if (&ht == this) return *this; // don't copy onto ourselves + clear(); + hash = ht.hash; + equals = ht.equals; + get_key = ht.get_key; + use_deleted = ht.use_deleted; + set_value(&delval, ht.delval); + copy_from(ht, HT_MIN_BUCKETS); // sets num_deleted to 0 too + return *this; + } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparse_hashtable& ht) { + STL_NAMESPACE::swap(hash, ht.hash); + STL_NAMESPACE::swap(equals, ht.equals); + STL_NAMESPACE::swap(get_key, ht.get_key); + STL_NAMESPACE::swap(num_deleted, ht.num_deleted); + STL_NAMESPACE::swap(use_deleted, ht.use_deleted); + STL_NAMESPACE::swap(enlarge_resize_percent, ht.enlarge_resize_percent); + STL_NAMESPACE::swap(shrink_resize_percent, ht.shrink_resize_percent); + { value_type tmp; // for annoying reasons, swap() doesn't work + set_value(&tmp, delval); + set_value(&delval, ht.delval); + set_value(&ht.delval, tmp); + } + table.swap(ht.table); + reset_thresholds(); + ht.reset_thresholds(); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + table.clear(); + reset_thresholds(); + num_deleted = 0; + } + + + // LOOKUP ROUTINES + private: + // Returns a pair of positions: 1st where the object is, 2nd where + // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET + // if object is not found; 2nd is ILLEGAL_BUCKET if it is. + // Note: because of deletions where-to-insert is not trivial: it's the + // first deleted bucket we see, as long as we don't find the key later + pair<size_type, size_type> find_position(const key_type &key) const { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + size_type insert_pos = ILLEGAL_BUCKET; // where we would insert + SPARSEHASH_STAT_UPDATE(total_lookups += 1); + while ( 1 ) { // probe until something happens + if ( !table.test(bucknum) ) { // bucket is empty + SPARSEHASH_STAT_UPDATE(total_probes += num_probes); + if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert + return pair<size_type,size_type>(ILLEGAL_BUCKET, bucknum); + else + return pair<size_type,size_type>(ILLEGAL_BUCKET, insert_pos); + + } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert + if ( insert_pos == ILLEGAL_BUCKET ) + insert_pos = bucknum; + + } else if ( equals(key, get_key(table.get(bucknum))) ) { + SPARSEHASH_STAT_UPDATE(total_probes += num_probes); + return pair<size_type,size_type>(bucknum, ILLEGAL_BUCKET); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count()); // don't probe too many times! + } + } + + public: + iterator find(const key_type& key) { + if ( size() == 0 ) return end(); + pair<size_type, size_type> pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return iterator(this, table.get_iter(pos.first), table.nonempty_end()); + } + + const_iterator find(const key_type& key) const { + if ( size() == 0 ) return end(); + pair<size_type, size_type> pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return const_iterator(this, + table.get_iter(pos.first), table.nonempty_end()); + } + + // Counts how many elements have key key. For maps, it's either 0 or 1. + size_type count(const key_type &key) const { + pair<size_type, size_type> pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? 0 : 1; + } + + // Likewise, equal_range doesn't really make sense for us. Oh well. + pair<iterator,iterator> equal_range(const key_type& key) { + const iterator pos = find(key); // either an iterator or end + return pair<iterator,iterator>(pos, pos); + } + pair<const_iterator,const_iterator> equal_range(const key_type& key) const { + const const_iterator pos = find(key); // either an iterator or end + return pair<iterator,iterator>(pos, pos); + } + + + // INSERTION ROUTINES + private: + // If you know *this is big enough to hold obj, use this routine + pair<iterator, bool> insert_noresize(const value_type& obj) { + // First, double-check we're not inserting delval + assert(!use_deleted || !equals(get_key(obj), get_key(delval))); + const pair<size_type,size_type> pos = find_position(get_key(obj)); + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return pair<iterator,bool>(iterator(this, table.get_iter(pos.first), + table.nonempty_end()), + false); // false: we didn't insert + } else { // pos.second says where to put it + if ( test_deleted(pos.second) ) { // just replace if it's been del. + // The set() below will undelete this object. We just worry about stats + assert(num_deleted > 0); + --num_deleted; // used to be, now it isn't + } + table.set(pos.second, obj); + return pair<iterator,bool>(iterator(this, table.get_iter(pos.second), + table.nonempty_end()), + true); // true: we did insert + } + } + + public: + // This is the normal insert routine, used by the outside world + pair<iterator, bool> insert(const value_type& obj) { + resize_delta(1); // adding an object, grow if need be + return insert_noresize(obj); + } + + // When inserting a lot at a time, we specialize on the type of iterator + template <class InputIterator> + void insert(InputIterator f, InputIterator l) { + // specializes on iterator type + insert(f, l, typename STL_NAMESPACE::iterator_traits<InputIterator>::iterator_category()); + } + + // Iterator supports operator-, resize before inserting + template <class ForwardIterator> + void insert(ForwardIterator f, ForwardIterator l, + STL_NAMESPACE::forward_iterator_tag) { + size_type n = STL_NAMESPACE::distance(f, l); // TODO(csilvers): standard? + resize_delta(n); + for ( ; n > 0; --n, ++f) + insert_noresize(*f); + } + + // Arbitrary iterator, can't tell how much to resize + template <class InputIterator> + void insert(InputIterator f, InputIterator l, + STL_NAMESPACE::input_iterator_tag) { + for ( ; f != l; ++f) + insert(*f); + } + + + // DELETION ROUTINES + size_type erase(const key_type& key) { + // First, double-check we're not erasing delval + assert(!use_deleted || !equals(key, get_key(delval))); + const_iterator pos = find(key); // shrug: shouldn't need to be const + if ( pos != end() ) { + assert(!test_deleted(pos)); // or find() shouldn't have returned it + set_deleted(pos); + ++num_deleted; + consider_shrink = true; // will think about shrink after next insert + return 1; // because we deleted one thing + } else { + return 0; // because we deleted nothing + } + } + + // This is really evil: really it should be iterator, not const_iterator. + // But...the only reason keys are const is to allow lookup. + // Since that's a moot issue for deleted keys, we allow const_iterators + void erase(const_iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + consider_shrink = true; // will think about shrink after next insert + } + } + + void erase(const_iterator f, const_iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + consider_shrink = true; // will think about shrink after next insert + } + + + // COMPARISON + bool operator==(const sparse_hashtable& ht) const { + // We really want to check that the hash functions are the same + // but alas there's no way to do this. We just hope. + return ( num_deleted == ht.num_deleted && table == ht.table ); + } + bool operator!=(const sparse_hashtable& ht) const { + return !(*this == ht); + } + + + // I/O + // We support reading and writing hashtables to disk. NOTE that + // this only stores the hashtable metadata, not the stuff you've + // actually put in the hashtable! Alas, since I don't know how to + // write a hasher or key_equal, you have to make sure everything + // but the table is the same. We compact before writing. + bool write_metadata(FILE *fp) { + squash_deleted(); // so we don't have to worry about delkey + return table.write_metadata(fp); + } + + bool read_metadata(FILE *fp) { + num_deleted = 0; // since we got rid before writing + bool result = table.read_metadata(fp); + reset_thresholds(); + return result; + } + + // Only meaningful if value_type is a POD. + bool write_nopointer_data(FILE *fp) { + return table.write_nopointer_data(fp); + } + + // Only meaningful if value_type is a POD. + bool read_nopointer_data(FILE *fp) { + return table.read_nopointer_data(fp); + } + + private: + // The actual data + hasher hash; // required by hashed_associative_container + key_equal equals; + ExtractKey get_key; + size_type num_deleted; // how many occupied buckets are marked deleted + bool use_deleted; // false until delval has been set + value_type delval; // which key marks deleted entries + float enlarge_resize_percent; // how full before resize + float shrink_resize_percent; // how empty before resize + size_type shrink_threshold; // table.size() * shrink_resize_percent + size_type enlarge_threshold; // table.size() * enlarge_resize_percent + sparsetable<value_type> table; // holds num_buckets and num_elements too + bool consider_shrink; // true if we should try to shrink before next insert + + void reset_thresholds() { + enlarge_threshold = static_cast<size_type>(table.size() + * enlarge_resize_percent); + shrink_threshold = static_cast<size_type>(table.size() + * shrink_resize_percent); + consider_shrink = false; // whatever caused us to reset already considered + } +}; + +// We need a global swap as well +template <class V, class K, class HF, class ExK, class EqK, class A> +inline void swap(sparse_hashtable<V,K,HF,ExK,EqK,A> &x, + sparse_hashtable<V,K,HF,ExK,EqK,A> &y) { + x.swap(y); +} + +#undef JUMP_ + +template <class V, class K, class HF, class ExK, class EqK, class A> +const typename sparse_hashtable<V,K,HF,ExK,EqK,A>::size_type + sparse_hashtable<V,K,HF,ExK,EqK,A>::ILLEGAL_BUCKET; + +// How full we let the table get before we resize. Knuth says .8 is +// good -- higher causes us to probe too much, though saves memory +template <class V, class K, class HF, class ExK, class EqK, class A> +const float sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.8f; + +// How empty we let the table get before we resize lower. +// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing +template <class V, class K, class HF, class ExK, class EqK, class A> +const float sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4f * +sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT; + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSEHASHTABLE_H_ */ diff --git a/3rdparty/google/sparsetable b/3rdparty/google/sparsetable new file mode 100644 index 0000000000..60f600392d --- /dev/null +++ b/3rdparty/google/sparsetable @@ -0,0 +1,1451 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A sparsetable is a random container that implements a sparse array, +// that is, an array that uses very little memory to store unassigned +// indices (in this case, between 1-2 bits per unassigned index). For +// instance, if you allocate an array of size 5 and assign a[2] = <big +// struct>, then a[2] will take up a lot of memory but a[0], a[1], +// a[3], and a[4] will not. Array elements that have a value are +// called "assigned". Array elements that have no value yet, or have +// had their value cleared using erase() or clear(), are called +// "unassigned". +// +// Unassigned values seem to have the default value of T (see below). +// Nevertheless, there is a difference between an unassigned index and +// one explicitly assigned the value of T(). The latter is considered +// assigned. +// +// Access to an array element is constant time, as is insertion and +// deletion. Insertion and deletion may be fairly slow, however: +// because of this container's memory economy, each insert and delete +// causes a memory reallocation. +// +// See /usr/(local/)?doc/sparsehash-0.1/sparsetable.html +// for information about how to use this class. + +#ifndef _SPARSETABLE_H_ +#define _SPARSETABLE_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <stdlib.h> // for malloc/free +#include <stdio.h> // to read/write tables +#ifdef HAVE_STDINT_H +#include <stdint.h> // the normal place uint16_t is defined +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> // the normal place u_int16_t is defined +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> // a third place for uint16_t or u_int16_t +#endif +#include <assert.h> // for bounds checking +#include <iterator> // to define reverse_iterator for me +#include <algorithm> // equal, lexicographical_compare, swap,... +#include <memory> // uninitialized_copy +#include <vector> // a sparsetable is a vector of groups +#include <google/type_traits.h> // for true_type, integral_constant, etc. + +#if STDC_HEADERS +#include <string.h> // for memcpy +#else +#if !HAVE_MEMCPY +#define memcpy(d, s, n) bcopy ((s), (d), (n)) +#endif +#endif + +_START_GOOGLE_NAMESPACE_ + +#ifndef HAVE_U_INT16_T +# if defined HAVE_UINT16_T + typedef uint16_t u_int16_t; // true on solaris, possibly other C99 libc's +# elif defined HAVE___UINT16 + typedef __int16 int16_t; // true on vc++7 + typedef unsigned __int16 u_int16_t; +# else + // Cannot find a 16-bit integer type. Hoping for the best with "short"... + typedef short int int16_t; + typedef unsigned short int u_int16_t; +# endif +#endif + +using STL_NAMESPACE::vector; +using STL_NAMESPACE::uninitialized_copy; + +// The smaller this is, the faster lookup is (because the group bitmap is +// smaller) and the faster insert is, because there's less to move. +// On the other hand, there are more groups. Since group::size_type is +// a short, this number should be of the form 32*x + 16 to avoid waste. +static const u_int16_t DEFAULT_SPARSEGROUP_SIZE = 48; // fits in 1.5 words + + +// A NOTE ON ASSIGNING: +// A sparse table does not actually allocate memory for entries +// that are not filled. Because of this, it becomes complicated +// to have a non-const iterator: we don't know, if the iterator points +// to a not-filled bucket, whether you plan to fill it with something +// or whether you plan to read its value (in which case you'll get +// the default bucket value). Therefore, while we can define const +// operations in a pretty 'normal' way, for non-const operations, we +// define something that returns a helper object with operator= and +// operator& that allocate a bucket lazily. We use this for table[] +// and also for regular table iterators. + +template <class tabletype> +class table_element_adaptor { + public: + typedef typename tabletype::value_type value_type; + typedef typename tabletype::size_type size_type; + typedef typename tabletype::reference reference; + typedef typename tabletype::pointer pointer; + + table_element_adaptor(tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + table_element_adaptor& operator= (const value_type &val) { + table->set(pos, val); + return *this; + } + operator value_type() { return table->get(pos); } // we look like a value + pointer operator& () { return &table->mutating_get(pos); } + + private: + tabletype* table; + size_type pos; +}; + +// Our iterator as simple as iterators can be: basically it's just +// the index into our table. Dereference, the only complicated +// thing, we punt to the table class. This just goes to show how +// much machinery STL requires to do even the most trivial tasks. +// +// By templatizing over tabletype, we have one iterator type which +// we can use for both sparsetables and sparsebins. In fact it +// works on any class that allows size() and operator[] (eg vector), +// as long as it does the standard STL typedefs too (eg value_type). + +template <class tabletype> +class table_iterator { + public: + typedef table_iterator iterator; + + typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + typedef table_element_adaptor<tabletype> reference; + typedef table_element_adaptor<tabletype>* pointer; + + // The "real" constructor + table_iterator(tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + // The default constructor, used when I define vars of type table::iterator + table_iterator() : table(NULL), pos(0) { } + // The copy constructor, for when I say table::iterator foo = tbl.begin() + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // The main thing our iterator does is dereference. If the table entry + // we point to is empty, we return the default value type. + // This is the big different function from the const iterator. + reference operator*() { + return table_element_adaptor<tabletype>(table, pos); + } + pointer operator->() { return &(operator*()); } + + // Helper function to assert things are ok; eg pos is still in range + void check() const { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that it's job. :-) + iterator& operator+=(size_type t) { pos += t; check(); return *this; } + iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + iterator& operator++() { ++pos; check(); return *this; } + iterator& operator--() { --pos; check(); return *this; } + iterator operator++(int) { iterator tmp(*this); // for x++ + ++pos; check(); return tmp; } + iterator operator--(int) { iterator tmp(*this); // for x-- + --pos; check(); return tmp; } + iterator operator+(difference_type i) const { iterator tmp(*this); + tmp += i; return tmp; } + iterator operator-(difference_type i) const { iterator tmp(*this); + tmp -= i; return tmp; } + difference_type operator-(iterator it) const { // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + reference operator[](difference_type n) const { + return *(*this + n); // simple though not totally efficient + } + + // Comparisons. + bool operator==(const iterator& it) const { + return table == it.table && pos == it.pos; + } + bool operator<(const iterator& it) const { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + bool operator!=(const iterator& it) const { return !(*this == it); } + bool operator<=(const iterator& it) const { return !(it < *this); } + bool operator>(const iterator& it) const { return it < *this; } + bool operator>=(const iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// support for "3 + iterator" has to be defined outside the class, alas +template<class T> +table_iterator<T> operator+(typename table_iterator<T>::difference_type i, + table_iterator<T> it) { + return it + i; // so people can say it2 = 3 + it +} + +template <class tabletype> +class const_table_iterator { + public: + typedef table_iterator<tabletype> iterator; + typedef const_table_iterator const_iterator; + + typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + typedef typename tabletype::const_reference reference; // we're const-only + typedef typename tabletype::const_pointer pointer; + + // The "real" constructor + const_table_iterator(const tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + // The default constructor, used when I define vars of type table::iterator + const_table_iterator() : table(NULL), pos(0) { } + // The copy constructor, for when I say table::iterator foo = tbl.begin() + // Also converts normal iterators to const iterators + const_table_iterator(const iterator &from) + : table(from.table), pos(from.pos) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // The main thing our iterator does is dereference. If the table entry + // we point to is empty, we return the default value type. + reference operator*() const { return (*table)[pos]; } + pointer operator->() const { return &(operator*()); } + + // Helper function to assert things are ok; eg pos is still in range + void check() const { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that it's job. :-) + const_iterator& operator+=(size_type t) { pos += t; check(); return *this; } + const_iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + const_iterator& operator++() { ++pos; check(); return *this; } + const_iterator& operator--() { --pos; check(); return *this; } + const_iterator operator++(int) { const_iterator tmp(*this); // for x++ + ++pos; check(); return tmp; } + const_iterator operator--(int) { const_iterator tmp(*this); // for x-- + --pos; check(); return tmp; } + const_iterator operator+(difference_type i) const { const_iterator tmp(*this); + tmp += i; return tmp; } + const_iterator operator-(difference_type i) const { const_iterator tmp(*this); + tmp -= i; return tmp; } + difference_type operator-(const_iterator it) const { // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + reference operator[](difference_type n) const { + return *(*this + n); // simple though not totally efficient + } + + // Comparisons. + bool operator==(const const_iterator& it) const { + return table == it.table && pos == it.pos; + } + bool operator<(const const_iterator& it) const { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + bool operator!=(const const_iterator& it) const { return !(*this == it); } + bool operator<=(const const_iterator& it) const { return !(it < *this); } + bool operator>(const const_iterator& it) const { return it < *this; } + bool operator>=(const const_iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + const tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// support for "3 + iterator" has to be defined outside the class, alas +template<class T> +const_table_iterator<T> operator+(typename + const_table_iterator<T>::difference_type i, + const_table_iterator<T> it) { + return it + i; // so people can say it2 = 3 + it +} + + +// --------------------------------------------------------------------------- + + +/* +// This is a 2-D iterator. You specify a begin and end over a list +// of *containers*. We iterate over each container by iterating over +// it. It's actually simple: +// VECTOR.begin() VECTOR[0].begin() --------> VECTOR[0].end() ---, +// | ________________________________________________/ +// | \_> VECTOR[1].begin() --------> VECTOR[1].end() -, +// | ___________________________________________________/ +// v \_> ...... +// VECTOR.end() +// +// It's impossible to do random access on one of these things in constant +// time, so it's just a bidirectional iterator. +// +// Unfortunately, because we need to use this for a non-empty iterator, +// we use nonempty_begin() and nonempty_end() instead of begin() and end() +// (though only going across, not down). +*/ + +#define TWOD_BEGIN_ nonempty_begin +#define TWOD_END_ nonempty_end +#define TWOD_ITER_ nonempty_iterator +#define TWOD_CONST_ITER_ const_nonempty_iterator + +template <class containertype> +class two_d_iterator { + public: + typedef two_d_iterator iterator; + + typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::reference reference; + typedef typename _tmp_vt::pointer pointer; + + // The "real" constructor. begin and end specify how many rows we have + // (in the diagram above); we always iterate over each row completely. + two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( row_current != row_end ) { + col_current = row_current->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + // If you want to start at an arbitrary place, you can, I guess + two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr, + typename containertype::value_type::TWOD_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + // The default constructor, used when I define vars of type table::iterator + two_d_iterator() : row_begin(), row_end(), row_current(), col_current() { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that it's job. :-) + // NOTE: this is not amortized constant time! What do we do about it? + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator& operator--() { + while ( row_current == row_end || + col_current == row_current->TWOD_BEGIN_() ) { + assert(row_current != row_begin); + --row_current; + col_current = row_current->TWOD_END_(); // this is 1 too far + } + --col_current; + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } + + + // Comparisons. + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } + + + // Here's the info we actually need to be an iterator + // These need to be public so we convert from iterator to const_iterator + typename containertype::iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_ITER_ col_current; +}; + +// The same thing again, but this time const. :-( +template <class containertype> +class const_two_d_iterator { + public: + typedef const_two_d_iterator iterator; + + typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::const_reference reference; + typedef typename _tmp_vt::const_pointer pointer; + + const_two_d_iterator(typename containertype::const_iterator begin, + typename containertype::const_iterator end, + typename containertype::const_iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( curr != end ) { + col_current = curr->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + const_two_d_iterator(typename containertype::const_iterator begin, + typename containertype::const_iterator end, + typename containertype::const_iterator curr, + typename containertype::value_type::TWOD_CONST_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + const_two_d_iterator() + : row_begin(), row_end(), row_current(), col_current() { + } + // Need this explicitly so we can convert normal iterators to const iterators + const_two_d_iterator(const two_d_iterator<containertype>& it) : + row_begin(it.row_begin), row_end(it.row_end), row_current(it.row_current), + col_current(it.col_current) { } + + typename containertype::const_iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_CONST_ITER_ col_current; + + + // EVERYTHING FROM HERE DOWN IS THE SAME AS THE NON-CONST ITERATOR + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator& operator--() { + while ( row_current == row_end || + col_current == row_current->TWOD_BEGIN_() ) { + assert(row_current != row_begin); + --row_current; + col_current = row_current->TWOD_END_(); // this is 1 too far + } + --col_current; + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } + + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } +}; + +// We provide yet another version, to be as frugal with memory as +// possible. This one frees each block of memory as it finishes +// iterating over it. By the end, the entire table is freed. +// For understandable reasons, you can only iterate over it once, +// which is why it's an input iterator +template <class containertype> +class destructive_two_d_iterator { + public: + typedef destructive_two_d_iterator iterator; + + typedef STL_NAMESPACE::input_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::reference reference; + typedef typename _tmp_vt::pointer pointer; + + destructive_two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( curr != end ) { + col_current = curr->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + destructive_two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr, + typename containertype::value_type::TWOD_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + destructive_two_d_iterator() + : row_begin(), row_end(), row_current(), col_current() { + } + + typename containertype::iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_ITER_ col_current; + + // This is the part that destroys + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + row_current->clear(); // the destructive part + // It would be nice if we could decrement sparsetable->num_buckets here + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + + // EVERYTHING FROM HERE DOWN IS THE SAME AS THE REGULAR ITERATOR + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } +}; + +#undef TWOD_BEGIN_ +#undef TWOD_END_ +#undef TWOD_ITER_ +#undef TWOD_CONST_ITER_ + + + + +// SPARSE-TABLE +// ------------ +// The idea is that a table with (logically) t buckets is divided +// into t/M *groups* of M buckets each. (M is a constant set in +// GROUP_SIZE for efficiency.) Each group is stored sparsely. +// Thus, inserting into the table causes some array to grow, which is +// slow but still constant time. Lookup involves doing a +// logical-position-to-sparse-position lookup, which is also slow but +// constant time. The larger M is, the slower these operations are +// but the less overhead (slightly). +// +// To store the sparse array, we store a bitmap B, where B[i] = 1 iff +// bucket i is non-empty. Then to look up bucket i we really look up +// array[# of 1s before i in B]. This is constant time for fixed M. +// +// Terminology: the position of an item in the overall table (from +// 1 .. t) is called its "location." The logical position in a group +// (from 1 .. M ) is called its "position." The actual location in +// the array (from 1 .. # of non-empty buckets in the group) is +// called its "offset." + +// The weird mod in the offset is entirely to quiet compiler warnings +// as is the cast to int after doing the "x mod 256" +#define PUT_(take_from, offset) do { \ + if (putc(static_cast<int>(((take_from) >> ((offset) % (sizeof(take_from)*8)))\ + % 256), fp) \ + == EOF) \ + return false; \ +} while (0) + +#define GET_(add_to, offset) do { \ + if ((x=getc(fp)) == EOF) \ + return false; \ + else \ + add_to |= (static_cast<size_type>(x) << ((offset) % (sizeof(add_to)*8))); \ +} while (0) + +template <class T, u_int16_t GROUP_SIZE> +class sparsegroup { + public: + // Basic types + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef table_iterator<sparsegroup<T, GROUP_SIZE> > iterator; + typedef const_table_iterator<sparsegroup<T, GROUP_SIZE> > const_iterator; + typedef table_element_adaptor<sparsegroup<T, GROUP_SIZE> > element_adaptor; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef u_int16_t size_type; // max # of buckets + typedef int16_t difference_type; + typedef STL_NAMESPACE::reverse_iterator<const_iterator> const_reverse_iterator; + typedef STL_NAMESPACE::reverse_iterator<iterator> reverse_iterator; + + // These are our special iterators, that go over non-empty buckets in a + // group. These aren't const-only because you can change non-empty bcks. + typedef pointer nonempty_iterator; + typedef const_pointer const_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator<nonempty_iterator> reverse_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator<const_nonempty_iterator> const_reverse_nonempty_iterator; + + // Iterator functions + iterator begin() { return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + iterator end() { return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + // We'll have versions for our special non-empty iterator too + nonempty_iterator nonempty_begin() { return group; } + const_nonempty_iterator nonempty_begin() const { return group; } + nonempty_iterator nonempty_end() { return group + num_buckets; } + const_nonempty_iterator nonempty_end() const { return group + num_buckets; } + reverse_nonempty_iterator nonempty_rbegin() { + return reverse_nonempty_iterator(nonempty_end()); + } + const_reverse_nonempty_iterator nonempty_rbegin() const { + return const_reverse_nonempty_iterator(nonempty_end()); + } + reverse_nonempty_iterator nonempty_rend() { + return reverse_nonempty_iterator(nonempty_begin()); + } + const_reverse_nonempty_iterator nonempty_rend() const { + return const_reverse_nonempty_iterator(nonempty_begin()); + } + + + // This gives us the "default" value to return for an empty bucket. + // We just use the default constructor on T, the template type + const_reference default_value() const { + static value_type defaultval = value_type(); + return defaultval; + } + + + private: + // We need to do all this bit manipulation, of course. ick + static size_type charbit(size_type i) { return i >> 3; } + static size_type modbit(size_type i) { return 1 << (i&7); } + int bmtest(size_type i) const { return bitmap[charbit(i)] & modbit(i); } + void bmset(size_type i) { bitmap[charbit(i)] |= modbit(i); } + void bmclear(size_type i) { bitmap[charbit(i)] &= ~modbit(i); } + + void* realloc_or_die(void* ptr, size_t num_bytes) { + void* retval = realloc(ptr, num_bytes); + if (retval == NULL) { + // We really should use PRIuS here, but I don't want to have to add + // a whole new configure option, with concomitant macro namespace + // pollution, just to print this (unlikely) error message. So I cast. + fprintf(stderr, "FATAL ERROR: failed to allocate %lu bytes for ptr %p", + static_cast<unsigned long>(num_bytes), ptr); + exit(1); + } + return retval; + } + + value_type* allocate_group(size_t n) { + return static_cast<value_type*>(realloc_or_die(NULL, + n * sizeof(value_type))); + } + + void free_group() { + // Valid even for empty group, because NULL+0 is defined to be NULL + value_type* end_it = group + num_buckets; + for (value_type* p = group; p != end_it; ++p) + p->~value_type(); + free(group); + group = NULL; + } + + public: // get_iter() in sparsetable needs it + // We need a small function that tells us how many set bits there are + // in positions 0..i-1 of the bitmap. It uses a big table. + // We make it static so templates don't allocate lots of these tables + static size_type pos_to_offset(const unsigned char *bm, size_type pos) { + // We could make these ints. The tradeoff is size (eg does it overwhelm + // the cache?) vs efficiency in referencing sub-word-sized array elements + static const char bits_in[256] = { // # of bits set in one char + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + }; + size_type retval = 0; + + // [Note: condition pos > 8 is an optimization; convince yourself we + // give exactly the same result as if we had pos >= 8 here instead.] + for ( ; pos > 8; pos -= 8 ) // bm[0..pos/8-1] + retval += bits_in[*bm++]; // chars we want *all* bits in + return retval + bits_in[*bm & ((1 << pos)-1)]; // the char that includes pos + } + + size_type pos_to_offset(size_type pos) const { // not static but still const + return pos_to_offset(bitmap, pos); + } + + + public: + // Constructors -- default and copy -- and destructor + sparsegroup() : group(0), num_buckets(0) { memset(bitmap, 0, sizeof(bitmap)); } + sparsegroup(const sparsegroup& x) : group(0), num_buckets(x.num_buckets) { + if ( num_buckets ) { + group = allocate_group(x.num_buckets); + uninitialized_copy(x.group, x.group + x.num_buckets, group); + } + memcpy(bitmap, x.bitmap, sizeof(bitmap)); + } + ~sparsegroup() { free_group(); } + + // Operator= is just like the copy constructor, I guess + // TODO(austern): Make this exception safe. Handle exceptions in value_type's + // copy constructor. + sparsegroup &operator=(const sparsegroup& x) { + if ( &x == this ) return *this; // x = x + if ( x.num_buckets == 0 ) { + free_group(); + } else { + value_type* p = allocate_group(x.num_buckets); + uninitialized_copy(x.group, x.group + x.num_buckets, p); + free_group(); + group = p; + } + memcpy(bitmap, x.bitmap, sizeof(bitmap)); + num_buckets = x.num_buckets; + return *this; + } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsegroup& x) { + STL_NAMESPACE::swap(group, x.group); + for ( int i = 0; i < sizeof(bitmap) / sizeof(*bitmap); ++i ) + STL_NAMESPACE::swap(bitmap[i], x.bitmap[i]); // swap not defined on arrays + STL_NAMESPACE::swap(num_buckets, x.num_buckets); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + free_group(); + memset(bitmap, 0, sizeof(bitmap)); + num_buckets = 0; + } + + // Functions that tell you about size. Alas, these aren't so useful + // because our table is always fixed size. + size_type size() const { return GROUP_SIZE; } + size_type max_size() const { return GROUP_SIZE; } + bool empty() const { return false; } + // We also may want to know how many *used* buckets there are + size_type num_nonempty() const { return num_buckets; } + + + // get()/set() are explicitly const/non-const. You can use [] if + // you want something that can be either (potentially more expensive). + const_reference get(size_type i) const { + if ( bmtest(i) ) // bucket i is occupied + return group[pos_to_offset(bitmap, i)]; + else + return default_value(); // return the default reference + } + + // TODO(csilvers): make protected + friend + reference mutating_get(size_type i) { // fills bucket i before getting + if ( !bmtest(i) ) + set(i, default_value()); + return group[pos_to_offset(bitmap, i)]; + } + + // Syntactic sugar. It's easy to return a const reference. To + // return a non-const reference, we need to use the assigner adaptor. + const_reference operator[](size_type i) const { + return get(i); + } + + element_adaptor operator[](size_type i) { + return element_adaptor(this, i); + } + + private: + // Create space at group[offset], assuming value_type has trivial + // copy constructor and destructor. (Really, we want it to have + // "trivial move", because that's what realloc and memmove both do. + // But there's no way to capture that using type_traits, so we + // pretend that move(x, y) is equivalent to "x.~T(); new(x) T(y);" + // which is pretty much correct, if a bit conservative.) + void set_aux(size_type offset, true_type) { + group = (value_type *) + realloc_or_die(group, sizeof(*group) * (num_buckets+1)); + // This is equivalent to memmove(), but faster on my Intel P4, + // at least with gcc4.1 -O2 / glibc 2.3.6. + for (size_type i = num_buckets; i > offset; --i) + memcpy(group + i, group + i-1, sizeof(*group)); + } + + // Create space at group[offset], without special assumptions about value_type + void set_aux(size_type offset, false_type) { + // This is valid because 0 <= offset <= num_buckets + value_type* p = allocate_group(num_buckets + 1); + uninitialized_copy(group, group + offset, p); + uninitialized_copy(group + offset, group + num_buckets, p + offset + 1); + free_group(); + group = p; + } + + public: + // This returns a reference to the inserted item (which is a copy of val). + // TODO(austern): Make this exception safe: handle exceptions from + // value_type's copy constructor. + reference set(size_type i, const_reference val) { + size_type offset = pos_to_offset(bitmap, i); // where we'll find (or insert) + if ( bmtest(i) ) { + // Delete the old value, which we're replacing with the new one + group[offset].~value_type(); + } else { + typedef integral_constant<bool, + (has_trivial_copy<value_type>::value && + has_trivial_destructor<value_type>::value)> + realloc_and_memmove_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)" + set_aux(offset, realloc_and_memmove_ok()); + ++num_buckets; + bmset(i); + } + // This does the actual inserting. Since we made the array using + // malloc, we use "placement new" to just call the constructor. + new(&group[offset]) value_type(val); + return group[offset]; + } + + // We let you see if a bucket is non-empty without retrieving it + bool test(size_type i) const { + return bmtest(i) ? true : false; // cast an int to a bool + } + bool test(iterator pos) const { + return bmtest(pos.pos) ? true : false; + } + + private: + // Shrink the array, assuming value_type has trivial copy + // constructor and destructor. (Really, we want it to have "trivial + // move", because that's what realloc and memmove both do. But + // there's no way to capture that using type_traits, so we pretend + // that move(x, y) is equivalent to ""x.~T(); new(x) T(y);" + // which is pretty much correct, if a bit conservative.) + void erase_aux(size_type offset, true_type) { + // This isn't technically necessary, since we know we have a + // trivial destructor, but is a cheap way to get a bit more safety. + group[offset].~value_type(); + // This is equivalent to memmove(), but faster on my Intel P4, + // at lesat with gcc4.1 -O2 / glibc 2.3.6. + assert(num_buckets > 0); + for (size_type i = offset; i < num_buckets-1; ++i) + memcpy(group + i, group + i+1, sizeof(*group)); // hopefully inlined! + group = (value_type *) + realloc_or_die(group, sizeof(*group) * (num_buckets-1)); + } + + // Shrink the array, without any special assumptions about value_type. + void erase_aux(size_type offset, false_type) { + // This is valid because 0 <= offset < num_buckets. Note the inequality. + value_type* p = allocate_group(num_buckets - 1); + uninitialized_copy(group, group + offset, p); + uninitialized_copy(group + offset + 1, group + num_buckets, p + offset); + free_group(); + group = p; + } + + public: + // This takes the specified elements out of the group. This is + // "undefining", rather than "clearing". + // TODO(austern): Make this exception safe: handle exceptions from + // value_type's copy constructor. + void erase(size_type i) { + if ( bmtest(i) ) { // trivial to erase empty bucket + size_type offset = pos_to_offset(bitmap,i); // where we'll find (or insert) + if ( num_buckets == 1 ) { + free_group(); + group = NULL; + } else { + typedef integral_constant<bool, + (has_trivial_copy<value_type>::value && + has_trivial_destructor<value_type>::value)> + realloc_and_memmove_ok; // pretend mv(x,y) == "x.~T(); new(x) T(y)" + erase_aux(offset, realloc_and_memmove_ok()); + } + --num_buckets; + bmclear(i); + } + } + + void erase(iterator pos) { + erase(pos.pos); + } + + void erase(iterator start_it, iterator end_it) { + // This could be more efficient, but to do so we'd need to make + // bmclear() clear a range of indices. Doesn't seem worth it. + for ( ; start_it != end_it; ++start_it ) + erase(start_it); + } + + + // I/O + // We support reading and writing groups to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the bitmap and size. Meant to be used with table I/O. + // Returns true if all was ok + bool write_metadata(FILE *fp) const { + assert(sizeof(num_buckets) == 2); // we explicitly set to u_int16_t + PUT_(num_buckets, 8); + PUT_(num_buckets, 0); + if ( !fwrite(bitmap, sizeof(bitmap), 1, fp) ) return false; + return true; + } + + // Reading destroys the old group contents! Returns true if all was ok + bool read_metadata(FILE *fp) { + clear(); + + int x; // the GET_ macro requires an 'int x' to be defined + GET_(num_buckets, 8); + GET_(num_buckets, 0); + + if ( !fread(bitmap, sizeof(bitmap), 1, fp) ) return false; + + // We'll allocate the space, but we won't fill it: it will be + // left as uninitialized raw memory. + group = allocate_group(num_buckets); + return true; + } + + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means POD and no pointers. + // However, we don't try to normalize endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return true; + } + + // When reading, we have to override the potential const-ness of *it. + // Again, only meaningful if value_type is a POD. + bool read_nopointer_data(FILE *fp) { + for ( nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fread(reinterpret_cast<void*>(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return true; + } + + // Comparisons. Note the comparisons are pretty arbitrary: we + // compare values of the first index that isn't equal (using default + // value for empty buckets). + bool operator==(const sparsegroup& x) const { + return ( num_buckets == x.num_buckets && + memcmp(bitmap, x.bitmap, sizeof(bitmap)) == 0 && + STL_NAMESPACE::equal(begin(), end(), x.begin()) ); // from algorithm + } + bool operator<(const sparsegroup& x) const { // also from algorithm + return STL_NAMESPACE::lexicographical_compare(begin(), end(), + x.begin(), x.end()); + } + bool operator!=(const sparsegroup& x) const { return !(*this == x); } + bool operator<=(const sparsegroup& x) const { return !(x < *this); } + bool operator>(const sparsegroup& x) const { return x < *this; } + bool operator>=(const sparsegroup& x) const { return !(*this < x); } + + private: + // The actual data + value_type *group; // (small) array of T's + unsigned char bitmap[(GROUP_SIZE-1)/8 + 1]; // fancy math is so we round up + size_type num_buckets; // limits GROUP_SIZE to 64K +}; + +// We need a global swap as well +template <class T, u_int16_t GROUP_SIZE> +inline void swap(sparsegroup<T,GROUP_SIZE> &x, sparsegroup<T,GROUP_SIZE> &y) { + x.swap(y); +} + +// --------------------------------------------------------------------------- + + +template <class T, u_int16_t GROUP_SIZE = DEFAULT_SPARSEGROUP_SIZE> +class sparsetable { + public: + // Basic types + typedef T value_type; // stolen from stl_vector.h + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef table_iterator<sparsetable<T, GROUP_SIZE> > iterator; + typedef const_table_iterator<sparsetable<T, GROUP_SIZE> > const_iterator; + typedef table_element_adaptor<sparsetable<T, GROUP_SIZE> > element_adaptor; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef STL_NAMESPACE::reverse_iterator<const_iterator> const_reverse_iterator; + typedef STL_NAMESPACE::reverse_iterator<iterator> reverse_iterator; + + // These are our special iterators, that go over non-empty buckets in a + // table. These aren't const only because you can change non-empty bcks. + typedef two_d_iterator< vector< sparsegroup<value_type, GROUP_SIZE> > > + nonempty_iterator; + typedef const_two_d_iterator< vector< sparsegroup<value_type, GROUP_SIZE> > > + const_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator<nonempty_iterator> reverse_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator<const_nonempty_iterator> const_reverse_nonempty_iterator; + // Another special iterator: it frees memory as it iterates (used to resize) + typedef destructive_two_d_iterator< vector< sparsegroup<value_type, GROUP_SIZE> > > + destructive_iterator; + + // Iterator functions + iterator begin() { return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + iterator end() { return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + // Versions for our special non-empty iterator + nonempty_iterator nonempty_begin() { + return nonempty_iterator(groups.begin(), groups.end(), groups.begin()); + } + const_nonempty_iterator nonempty_begin() const { + return const_nonempty_iterator(groups.begin(),groups.end(), groups.begin()); + } + nonempty_iterator nonempty_end() { + return nonempty_iterator(groups.begin(), groups.end(), groups.end()); + } + const_nonempty_iterator nonempty_end() const { + return const_nonempty_iterator(groups.begin(), groups.end(), groups.end()); + } + reverse_nonempty_iterator nonempty_rbegin() { + return reverse_nonempty_iterator(nonempty_end()); + } + const_reverse_nonempty_iterator nonempty_rbegin() const { + return const_reverse_nonempty_iterator(nonempty_end()); + } + reverse_nonempty_iterator nonempty_rend() { + return reverse_nonempty_iterator(nonempty_begin()); + } + const_reverse_nonempty_iterator nonempty_rend() const { + return const_reverse_nonempty_iterator(nonempty_begin()); + } + destructive_iterator destructive_begin() { + return destructive_iterator(groups.begin(), groups.end(), groups.begin()); + } + destructive_iterator destructive_end() { + return destructive_iterator(groups.begin(), groups.end(), groups.end()); + } + + private: + typedef typename vector< sparsegroup<value_type, GROUP_SIZE> >::reference + GroupsReference; + typedef typename + vector< sparsegroup<value_type, GROUP_SIZE> >::const_reference + GroupsConstReference; + typedef typename vector< sparsegroup<value_type, GROUP_SIZE> >::iterator + GroupsIterator; + typedef typename vector< sparsegroup<value_type, GROUP_SIZE> >::const_iterator + GroupsConstIterator; + + // How to deal with the proper group + static size_type num_groups(size_type num) { // how many to hold num buckets + return num == 0 ? 0 : ((num-1) / GROUP_SIZE) + 1; + } + + u_int16_t pos_in_group(size_type i) const { + return static_cast<u_int16_t>(i % GROUP_SIZE); + } + size_type group_num(size_type i) const { + return i / GROUP_SIZE; + } + GroupsReference which_group(size_type i) { + return groups[group_num(i)]; + } + GroupsConstReference which_group(size_type i) const { + return groups[group_num(i)]; + } + + public: + // Constructors -- default, normal (when you specify size), and copy + sparsetable(size_type sz = 0) + : groups(num_groups(sz)), table_size(sz), num_buckets(0) { } + // We'll can get away with using the default copy constructor, + // and default destructor, and hence the default operator=. Huzzah! + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsetable& x) { + STL_NAMESPACE::swap(groups, x.groups); + STL_NAMESPACE::swap(table_size, x.table_size); + STL_NAMESPACE::swap(num_buckets, x.num_buckets); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + GroupsIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) { + group->clear(); + } + num_buckets = 0; + } + + // Functions that tell you about size. + // NOTE: empty() is non-intuitive! It does not tell you the number + // of not-empty buckets (use num_nonempty() for that). Instead + // it says whether you've allocated any buckets or not. + size_type size() const { return table_size; } + size_type max_size() const { return size_type(-1); } + bool empty() const { return table_size == 0; } + // We also may want to know how many *used* buckets there are + size_type num_nonempty() const { return num_buckets; } + + // OK, we'll let you resize one of these puppies + void resize(size_type new_size) { + groups.resize(num_groups(new_size)); + if ( new_size < table_size) { // lower num_buckets, clear last group + if ( pos_in_group(new_size) > 0 ) // need to clear inside last group + groups.back().erase(groups.back().begin() + pos_in_group(new_size), + groups.back().end()); + num_buckets = 0; // refigure # of used buckets + GroupsConstIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + num_buckets += group->num_nonempty(); + } + table_size = new_size; + } + + + // We let you see if a bucket is non-empty without retrieving it + bool test(size_type i) const { + return which_group(i).test(pos_in_group(i)); + } + bool test(iterator pos) const { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + bool test(const_iterator pos) const { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + + // We only return const_references because it's really hard to + // return something settable for empty buckets. Use set() instead. + const_reference get(size_type i) const { + assert(i < table_size); + return which_group(i).get(pos_in_group(i)); + } + + // TODO(csilvers): make protected + friend element_adaptor + reference mutating_get(size_type i) { // fills bucket i before getting + assert(i < table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + reference retval = which_group(i).mutating_get(pos_in_group(i)); + num_buckets += which_group(i).num_nonempty() - old_numbuckets; + return retval; + } + + // Syntactic sugar. As in sparsegroup, the non-const version is harder + const_reference operator[](size_type i) const { + return get(i); + } + + element_adaptor operator[](size_type i) { + return element_adaptor(this, i); + } + + // Needed for hashtables, gets as a nonempty_iterator. Crashes for empty bcks + const_nonempty_iterator get_iter(size_type i) const { + assert(test(i)); // how can a nonempty_iterator point to an empty bucket? + return const_nonempty_iterator( + groups.begin(), groups.end(), + groups.begin() + group_num(i), + (groups[group_num(i)].nonempty_begin() + + groups[group_num(i)].pos_to_offset(pos_in_group(i)))); + } + // For nonempty we can return a non-const version + nonempty_iterator get_iter(size_type i) { + assert(test(i)); // how can a nonempty_iterator point to an empty bucket? + return nonempty_iterator( + groups.begin(), groups.end(), + groups.begin() + group_num(i), + (groups[group_num(i)].nonempty_begin() + + groups[group_num(i)].pos_to_offset(pos_in_group(i)))); + } + + + // This returns a reference to the inserted item (which is a copy of val) + // The trick is to figure out whether we're replacing or inserting anew + reference set(size_type i, const_reference val) { + assert(i < table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + reference retval = which_group(i).set(pos_in_group(i), val); + num_buckets += which_group(i).num_nonempty() - old_numbuckets; + return retval; + } + + // This takes the specified elements out of the table. This is + // "undefining", rather than "clearing". + void erase(size_type i) { + assert(i < table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + which_group(i).erase(pos_in_group(i)); + num_buckets += which_group(i).num_nonempty() - old_numbuckets; + } + + void erase(iterator pos) { + erase(pos.pos); + } + + void erase(iterator start_it, iterator end_it) { + // This could be more efficient, but then we'd need to figure + // out if we spanned groups or not. Doesn't seem worth it. + for ( ; start_it != end_it; ++start_it ) + erase(start_it); + } + + + // We support reading and writing tables to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the groups and sizes. Returns true if all went ok. + + private: + // Every time the disk format changes, this should probably change too + static const unsigned long MAGIC_NUMBER = 0x24687531; + + // Old versions of this code write all data in 32 bits. We need to + // support these files as well as having support for 64-bit systems. + // So we use the following encoding scheme: for values < 2^32-1, we + // store in 4 bytes in big-endian order. For values > 2^32, we + // store 0xFFFFFFF followed by 8 bytes in big-endian order. This + // causes us to mis-read old-version code that stores exactly + // 0xFFFFFFF, but I don't think that is likely to have happened for + // these particular values. + static bool write_32_or_64(FILE* fp, size_type value) { + if ( value < 0xFFFFFFFFULL ) { // fits in 4 bytes + PUT_(value, 24); + PUT_(value, 16); + PUT_(value, 8); + PUT_(value, 0); + } else if ( value == 0xFFFFFFFFUL ) { // special case in 32bit systems + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker + PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); + } else { + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker + PUT_(value, 56); + PUT_(value, 48); + PUT_(value, 40); + PUT_(value, 32); + PUT_(value, 24); + PUT_(value, 16); + PUT_(value, 8); + PUT_(value, 0); + } + return true; + } + + static bool read_32_or_64(FILE* fp, size_type *value) { // reads into value + size_type first4 = 0; + int x; + GET_(first4, 24); + GET_(first4, 16); + GET_(first4, 8); + GET_(first4, 0); + if ( first4 < 0xFFFFFFFFULL ) { + *value = first4; + } else { + GET_(*value, 56); + GET_(*value, 48); + GET_(*value, 40); + GET_(*value, 32); + GET_(*value, 24); + GET_(*value, 16); + GET_(*value, 8); + GET_(*value, 0); + } + return true; + } + + public: + bool write_metadata(FILE *fp) const { + if ( !write_32_or_64(fp, MAGIC_NUMBER) ) return false; + if ( !write_32_or_64(fp, table_size) ) return false; + if ( !write_32_or_64(fp, num_buckets) ) return false; + + GroupsConstIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + if ( group->write_metadata(fp) == false ) return false; + return true; + } + + // Reading destroys the old table contents! Returns true if read ok. + bool read_metadata(FILE *fp) { + size_type magic_read = 0; + if ( !read_32_or_64(fp, &magic_read) ) return false; + if ( magic_read != MAGIC_NUMBER ) { + clear(); // just to be consistent + return false; + } + + if ( !read_32_or_64(fp, &table_size) ) return false; + if ( !read_32_or_64(fp, &num_buckets) ) return false; + + resize(table_size); // so the vector's sized ok + GroupsIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + if ( group->read_metadata(fp) == false ) return false; + return true; + } + + // This code is identical to that for SparseGroup + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means no pointers. + // However, we don't try to normalize endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return true; + } + + // When reading, we have to override the potential const-ness of *it + bool read_nopointer_data(FILE *fp) { + for ( nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fread(reinterpret_cast<void*>(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return true; + } + + // Comparisons. Note the comparisons are pretty arbitrary: we + // compare values of the first index that isn't equal (using default + // value for empty buckets). + bool operator==(const sparsetable& x) const { + return ( table_size == x.table_size && + num_buckets == x.num_buckets && + groups == x.groups ); + } + bool operator<(const sparsetable& x) const { // also from algobase.h + return STL_NAMESPACE::lexicographical_compare(begin(), end(), + x.begin(), x.end()); + } + bool operator!=(const sparsetable& x) const { return !(*this == x); } + bool operator<=(const sparsetable& x) const { return !(x < *this); } + bool operator>(const sparsetable& x) const { return x < *this; } + bool operator>=(const sparsetable& x) const { return !(*this < x); } + + + private: + // The actual data + vector< sparsegroup<value_type, GROUP_SIZE> > groups; // our list of groups + size_type table_size; // how many buckets they want + size_type num_buckets; // number of non-empty buckets +}; + +// We need a global swap as well +template <class T, u_int16_t GROUP_SIZE> +inline void swap(sparsetable<T,GROUP_SIZE> &x, sparsetable<T,GROUP_SIZE> &y) { + x.swap(y); +} + +#undef GET_ +#undef PUT_ + +_END_GOOGLE_NAMESPACE_ + +#endif diff --git a/3rdparty/google/type_traits.h b/3rdparty/google/type_traits.h new file mode 100644 index 0000000000..5f88133b95 --- /dev/null +++ b/3rdparty/google/type_traits.h @@ -0,0 +1,250 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ---- +// Author: Matt Austern +// +// Define a small subset of tr1 type traits. The traits we define are: +// is_integral +// is_floating_point +// is_pointer +// is_reference +// is_pod +// has_trivial_constructor +// has_trivial_copy +// has_trivial_assign +// has_trivial_destructor +// remove_const +// remove_volatile +// remove_cv +// remove_reference +// remove_pointer +// is_convertible +// We can add more type traits as required. + +#ifndef BASE_TYPE_TRAITS_H_ +#define BASE_TYPE_TRAITS_H_ + +#include <google/sparsehash/sparseconfig.h> +#include <utility> // For pair + +_START_GOOGLE_NAMESPACE_ + +// integral_constant, defined in tr1, is a wrapper for an integer +// value. We don't really need this generality; we could get away +// with hardcoding the integer type to bool. We use the fully +// general integer_constant for compatibility with tr1. + +template<class T, T v> +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant<T, v> type; +}; + +template <class T, T v> const T integral_constant<T, v>::value; + +// Abbreviations: true_type and false_type are structs that represent +// boolean true and false values. +typedef integral_constant<bool, true> true_type; +typedef integral_constant<bool, false> false_type; + +// Types small_ and big_ are guaranteed such that sizeof(small_) < +// sizeof(big_) +typedef char small_; + +struct big_ { + char dummy[2]; +}; + +// is_integral is false except for the built-in integer types. +template <class T> struct is_integral : false_type { }; +template<> struct is_integral<bool> : true_type { }; +template<> struct is_integral<char> : true_type { }; +template<> struct is_integral<unsigned char> : true_type { }; +template<> struct is_integral<signed char> : true_type { }; +#if defined(_MSC_VER) +// wchar_t is not by default a distinct type from unsigned short in +// Microsoft C. +// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx +template<> struct is_integral<__wchar_t> : true_type { }; +#else +template<> struct is_integral<wchar_t> : true_type { }; +#endif +template<> struct is_integral<short> : true_type { }; +template<> struct is_integral<unsigned short> : true_type { }; +template<> struct is_integral<int> : true_type { }; +template<> struct is_integral<unsigned int> : true_type { }; +template<> struct is_integral<long> : true_type { }; +template<> struct is_integral<unsigned long> : true_type { }; +#ifdef HAVE_LONG_LONG +template<> struct is_integral<long long> : true_type { }; +template<> struct is_integral<unsigned long long> : true_type { }; +#endif + + +// is_floating_point is false except for the built-in floating-point types. +template <class T> struct is_floating_point : false_type { }; +template<> struct is_floating_point<float> : true_type { }; +template<> struct is_floating_point<double> : true_type { }; +template<> struct is_floating_point<long double> : true_type { }; + + +// is_pointer is false except for pointer types. +template <class T> struct is_pointer : false_type { }; +template <class T> struct is_pointer<T*> : true_type { }; + + +// is_reference is false except for reference types. +template<typename T> struct is_reference : false_type {}; +template<typename T> struct is_reference<T&> : true_type {}; + + +// We can't get is_pod right without compiler help, so fail conservatively. +// We will assume it's false except for arithmetic types and pointers, +// and const versions thereof. Note that std::pair is not a POD. +template <class T> struct is_pod + : integral_constant<bool, (is_integral<T>::value || + is_floating_point<T>::value || + is_pointer<T>::value)> { }; +template <class T> struct is_pod<const T> : is_pod<T> { }; + + +// We can't get has_trivial_constructor right without compiler help, so +// fail conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial +// constructors. (3) array of a type with a trivial constructor. +// (4) const versions thereof. +template <class T> struct has_trivial_constructor : is_pod<T> { }; +template <class T, class U> struct has_trivial_constructor<std::pair<T, U> > + : integral_constant<bool, + (has_trivial_constructor<T>::value && + has_trivial_constructor<U>::value)> { }; +template <class A, int N> struct has_trivial_constructor<A[N]> + : has_trivial_constructor<A> { }; +template <class T> struct has_trivial_constructor<const T> + : has_trivial_constructor<T> { }; + +// We can't get has_trivial_copy right without compiler help, so fail +// conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial copy +// constructors. (3) array of a type with a trivial copy constructor. +// (4) const versions thereof. +template <class T> struct has_trivial_copy : is_pod<T> { }; +template <class T, class U> struct has_trivial_copy<std::pair<T, U> > + : integral_constant<bool, + (has_trivial_copy<T>::value && + has_trivial_copy<U>::value)> { }; +template <class A, int N> struct has_trivial_copy<A[N]> + : has_trivial_copy<A> { }; +template <class T> struct has_trivial_copy<const T> : has_trivial_copy<T> { }; + +// We can't get has_trivial_assign right without compiler help, so fail +// conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial copy +// constructors. (3) array of a type with a trivial assign constructor. +template <class T> struct has_trivial_assign : is_pod<T> { }; +template <class T, class U> struct has_trivial_assign<std::pair<T, U> > + : integral_constant<bool, + (has_trivial_assign<T>::value && + has_trivial_assign<U>::value)> { }; +template <class A, int N> struct has_trivial_assign<A[N]> + : has_trivial_assign<A> { }; + +// We can't get has_trivial_destructor right without compiler help, so +// fail conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial +// destructors. (3) array of a type with a trivial destructor. +// (4) const versions thereof. +template <class T> struct has_trivial_destructor : is_pod<T> { }; +template <class T, class U> struct has_trivial_destructor<std::pair<T, U> > + : integral_constant<bool, + (has_trivial_destructor<T>::value && + has_trivial_destructor<U>::value)> { }; +template <class A, int N> struct has_trivial_destructor<A[N]> + : has_trivial_destructor<A> { }; +template <class T> struct has_trivial_destructor<const T> + : has_trivial_destructor<T> { }; + +// Specified by TR1 [4.7.1] +template<typename T> struct remove_const { typedef T type; }; +template<typename T> struct remove_const<T const> { typedef T type; }; +template<typename T> struct remove_volatile { typedef T type; }; +template<typename T> struct remove_volatile<T volatile> { typedef T type; }; +template<typename T> struct remove_cv { + typedef typename remove_const<typename remove_volatile<T>::type>::type type; +}; + + +// Specified by TR1 [4.7.2] +template<typename T> struct remove_reference { typedef T type; }; +template<typename T> struct remove_reference<T&> { typedef T type; }; + +// Specified by TR1 [4.7.4] Pointer modifications. +template<typename T> struct remove_pointer { typedef T type; }; +template<typename T> struct remove_pointer<T*> { typedef T type; }; +template<typename T> struct remove_pointer<T* const> { typedef T type; }; +template<typename T> struct remove_pointer<T* volatile> { typedef T type; }; +template<typename T> struct remove_pointer<T* const volatile> { + typedef T type; }; + +// Specified by TR1 [4.6] Relationships between types +#ifndef _MSC_VER +namespace internal { + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +template <typename From, typename To> +struct ConvertHelper { + static small_ Test(To); + static big_ Test(...); + static From Create(); +}; +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +template <typename From, typename To> +struct is_convertible + : integral_constant<bool, + sizeof(internal::ConvertHelper<From, To>::Test( + internal::ConvertHelper<From, To>::Create())) + == sizeof(small_)> { +}; +#endif + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_TYPE_TRAITS_H_ diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index 310079f376..c3a520538d 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -130,13 +130,13 @@ int hwMFIFOWrite(u32 addr, u8 *data, u32 size) { /* it does, so first copy 's1' bytes from 'data' to 'addr' */ dst = (u8*)PSM(addr); if (dst == NULL) return -1; - Cpu->Clear(addr, s1/4); + //Cpu->Clear(addr, s1/4); memcpy_fast(dst, data, s1); /* and second copy 's2' bytes from '&data[s1]' to 'maddr' */ dst = (u8*)PSM(psHu32(DMAC_RBOR)); if (dst == NULL) return -1; - Cpu->Clear(psHu32(DMAC_RBOR), s2/4); + //Cpu->Clear(psHu32(DMAC_RBOR), s2/4); memcpy_fast(dst, &data[s1], s2); } else { //u32 * tempptr, * tempptr2; @@ -144,7 +144,7 @@ int hwMFIFOWrite(u32 addr, u8 *data, u32 size) { /* it doesn't, so just copy 'size' bytes from 'data' to 'addr' */ dst = (u8*)PSM(addr); if (dst == NULL) return -1; - Cpu->Clear(addr, size/4); + //Cpu->Clear(addr, size/4); memcpy_fast(dst, data, size); } diff --git a/pcsx2/MemoryVM.cpp b/pcsx2/MemoryVM.cpp deleted file mode 100644 index 132b4db9ce..0000000000 --- a/pcsx2/MemoryVM.cpp +++ /dev/null @@ -1,2140 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator - * Copyright (C) 2002-2009 Pcsx2 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 - */ - -// Virtual memory model for Pcsx2. -// This module is left in primarily as a reference for the implementation of constant -// propagation. - -#include "PrecompiledHeader.h" -#include "Common.h" -#include "iR5900.h" - -#include "PsxCommon.h" -#include "VUmicro.h" -#include "GS.h" -#include "vtlb.h" -#include "IPU/IPU.h" - -#ifdef PCSX2_VIRTUAL_MEM -#include "iR3000A.h" // VM handles both Iop and EE memory from here. >_< -#include "Counters.h" -#endif - -#pragma warning(disable:4799) // No EMMS at end of function - -#ifdef ENABLECACHE -#include "Cache.h" -#endif - -#ifdef __LINUX__ -#include <sys/mman.h> -#endif - -///////////////////////////// -// VIRTUAL MEM START -///////////////////////////// -#ifdef PCSX2_VIRTUAL_MEM - -class vm_alloc_failed_exception : public std::runtime_error -{ -public: - void* requested_addr; - int requested_size; - void* returned_addr; - - explicit vm_alloc_failed_exception( void* reqadr, uint reqsize, void* retadr ) : - std::runtime_error( "virtual memory allocation failure." ) - , requested_addr( reqadr ) - , requested_size( reqsize ) - , returned_addr( retadr ) - {} -}; - -PSMEMORYBLOCK s_psM = {0}, s_psHw = {0}, s_psS = {0}, s_psxM = {0}, s_psVuMem = {0}; - -static void PHYSICAL_ALLOC( void* ptr, uint size, PSMEMORYBLOCK& block) -{ - if(SysPhysicalAlloc(size, &block) == -1 ) - throw vm_alloc_failed_exception( ptr, size, NULL ); - if(SysVirtualPhyAlloc(ptr, size, &block) == -1) - throw vm_alloc_failed_exception( ptr, size, NULL ); -} - -static void PHYSICAL_FREE( void* ptr, uint size, PSMEMORYBLOCK& block) -{ - SysVirtualFree(ptr, size); - SysPhysicalFree(&block); -} - - -#ifdef _WIN32 // windows implementation of vm - -static PSMEMORYMAP initMemoryMap(uptr* aPFNs, uptr* aVFNs) -{ - PSMEMORYMAP m; - m.aPFNs = aPFNs; - m.aVFNs = aVFNs; - return m; -} - -// only do vm hack for release -#ifndef PCSX2_DEVBUILD -#define VM_HACK -#endif - -// virtual memory blocks -PSMEMORYMAP *memLUT = NULL; - -static void VIRTUAL_ALLOC( void* base, uint size, uint Protection) -{ - LPVOID lpMemReserved = VirtualAlloc( base, size, MEM_RESERVE|MEM_COMMIT, Protection ); - if( base != lpMemReserved ) - throw vm_alloc_failed_exception( base, size, lpMemReserved ); -} - -static void ReserveExtraMem( void* base, uint size ) -{ - void* pExtraMem = VirtualAlloc(base, size, MEM_RESERVE|MEM_PHYSICAL, PAGE_READWRITE); - if( pExtraMem != base ) - throw vm_alloc_failed_exception( base, size, pExtraMem); -} - -void memAlloc() -{ - LPVOID pExtraMem = NULL; - - // release the previous reserved mem - VirtualFree(PS2MEM_BASE, 0, MEM_RELEASE); - - try - { - // allocate all virtual memory - PHYSICAL_ALLOC(PS2MEM_BASE, Ps2MemSize::Base, s_psM); - VIRTUAL_ALLOC(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READONLY); - VIRTUAL_ALLOC(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READONLY); - VIRTUAL_ALLOC(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READONLY); - VIRTUAL_ALLOC(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READONLY); - PHYSICAL_ALLOC(PS2MEM_SCRATCH, Ps2MemSize::Scratch, s_psS); - PHYSICAL_ALLOC(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); - PHYSICAL_ALLOC(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); - PHYSICAL_ALLOC(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); - - VIRTUAL_ALLOC(PS2MEM_PSXHW, Ps2MemSize::IopHardware, PAGE_READWRITE); - //VIRTUAL_ALLOC(PS2MEM_PSXHW2, 0x00010000, PAGE_READWRITE); - VIRTUAL_ALLOC(PS2MEM_PSXHW4, 0x00010000, PAGE_NOACCESS); - VIRTUAL_ALLOC(PS2MEM_GS, 0x00002000, PAGE_READWRITE); - VIRTUAL_ALLOC(PS2MEM_DEV9, 0x00010000, PAGE_NOACCESS); - VIRTUAL_ALLOC(PS2MEM_SPU2, 0x00010000, PAGE_NOACCESS); - VIRTUAL_ALLOC(PS2MEM_SPU2_, 0x00010000, PAGE_NOACCESS); - - VIRTUAL_ALLOC(PS2MEM_B80, 0x00010000, PAGE_READWRITE); - VIRTUAL_ALLOC(PS2MEM_BA0, 0x00010000, PAGE_READWRITE); - - // reserve the left over 224Mb, don't map - ReserveExtraMem( PS2MEM_BASE+Ps2MemSize::Base, 0x0e000000 ); - - // reserve left over psx mem - ReserveExtraMem( PS2MEM_PSX+Ps2MemSize::IopRam, 0x00600000 ); - - // reserve gs mem - ReserveExtraMem( PS2MEM_BASE+0x20000000, 0x10000000 ); - - // special addrs mmap - VIRTUAL_ALLOC(PS2MEM_BASE+0x5fff0000, 0x10000, PAGE_READWRITE); - - // alloc virtual mappings - if( memLUT == NULL ) - memLUT = (PSMEMORYMAP*)_aligned_malloc(0x100000 * sizeof(PSMEMORYMAP), 16); - if( memLUT == NULL ) - throw Exception::OutOfMemory( "memAlloc VM > failed to allocated memory for LUT." ); - } - catch( vm_alloc_failed_exception& ex ) - { - Console::Error( "Virtual Memory Error > Cannot reserve %dk memory block at 0x%8.8x", params - ex.requested_size / 1024, ex.requested_addr ); - - Console::Error( "\tError code: %d \tReturned address: 0x%8.8x", params - GetLastError(), ex.returned_addr); - - memShutdown(); - } - catch( std::exception& ) - { - memShutdown(); - } -} - -void memShutdown() -{ - // Free up the "extra mem" reservations - VirtualFree(PS2MEM_BASE+Ps2MemSize::Base, 0, MEM_RELEASE); - VirtualFree(PS2MEM_PSX+Ps2MemSize::IopRam, 0, MEM_RELEASE); - VirtualFree(PS2MEM_BASE+0x20000000, 0, MEM_RELEASE); // GS reservation - - PHYSICAL_FREE(PS2MEM_BASE, Ps2MemSize::Base, s_psM); - SysMunmap(PS2MEM_ROM, Ps2MemSize::Rom); - SysMunmap(PS2MEM_ROM1, Ps2MemSize::Rom1); - SysMunmap(PS2MEM_ROM2, Ps2MemSize::Rom2); - SysMunmap(PS2MEM_EROM, Ps2MemSize::ERom); - PHYSICAL_FREE(PS2MEM_SCRATCH, Ps2MemSize::Scratch, s_psS); - PHYSICAL_FREE(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); - PHYSICAL_FREE(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); - PHYSICAL_FREE(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); - - SysMunmap(PS2MEM_VU0MICRO, 0x00010000); // allocate for all VUs - - SysMunmap(PS2MEM_PSXHW, Ps2MemSize::IopHardware); - //SysMunmap(PS2MEM_PSXHW2, 0x00010000); - SysMunmap(PS2MEM_PSXHW4, 0x00010000); - SysMunmap(PS2MEM_GS, 0x00002000); - SysMunmap(PS2MEM_DEV9, 0x00010000); - SysMunmap(PS2MEM_SPU2, 0x00010000); - SysMunmap(PS2MEM_SPU2_, 0x00010000); - - SysMunmap(PS2MEM_B80, 0x00010000); - SysMunmap(PS2MEM_BA0, 0x00010000); - - // Special Addrs.. ? - SysMunmap(PS2MEM_BASE+0x5fff0000, 0x10000); - - VirtualFree(PS2MEM_VU0MICRO, 0, MEM_RELEASE); - - safe_aligned_free( memLUT ); - - // reserve mem - VirtualAlloc(PS2MEM_BASE, 0x40000000, MEM_RESERVE, PAGE_NOACCESS); -} - -//NOTE: A lot of the code reading depends on the registers being less than 8 -// MOV8 88/8A -// MOV16 6689 -// MOV32 89/8B -// SSEMtoR64 120f -// SSERtoM64 130f -// SSEMtoR128 280f -// SSERtoM128 290f - -#define SKIP_WRITE() { \ - switch(code&0xff) { \ - case 0x88: \ - if( !(code&0x8000) ) goto DefaultHandler; \ - ContextRecord->Eip += 6; \ - break; \ - case 0x66: \ - assert( code&0x800000 ); \ - assert( (code&0xffff) == 0x8966 ); \ - ContextRecord->Eip += 7; \ - break; \ - case 0x89: \ - assert( code&0x8000 ); \ - ContextRecord->Eip += 6; \ - break; \ - case 0x0f: /* 130f, 230f*/ \ - assert( (code&0xffff) == 0x290f || (code&0xffff) == 0x130f ); \ - assert( code&0x800000 ); \ - ContextRecord->Eip += 7; \ - break; \ - default: \ - goto DefaultHandler; \ -} \ -} \ - -#define SKIP_READ() { \ - switch(code&0xff) { \ - case 0x8A: \ - if( !(code&0x8000) ) goto DefaultHandler; \ - ContextRecord->Eip += 6; \ - rd = (code>>(8+3))&7; \ - break; \ - case 0x66: \ - if( (code&0x07000000) == 0x05000000 ) ContextRecord->Eip += 8; /* 8 for mem reads*/ \ - else ContextRecord->Eip += 4 + ((code&0x1f000000) == 0x0c000000) + !!(code&0x40000000); \ - rd = (code>>(24+3))&7; \ - break; \ - case 0x8B: \ - if( !(code&0x8000) ) goto DefaultHandler; \ - ContextRecord->Eip += 6; \ - rd = (code>>(8+3))&7; \ - break; \ - case 0x0f: { \ - assert( (code&0xffff)==0x120f || (code&0xffff)==0x280f || (code&0xffff) == 0xb60f || (code&0xffff) == 0xb70f ); \ - if( !(code&0x800000) ) goto DefaultHandler; \ - ContextRecord->Eip += 7; \ - rd = (code>>(16+3))&7; \ - break; } \ - default: \ - goto DefaultHandler; \ -} \ -} \ - -int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps) -{ - struct _EXCEPTION_RECORD* ExceptionRecord = eps->ExceptionRecord; - struct _CONTEXT* ContextRecord = eps->ContextRecord; - - u32 addr; - - C_ASSERT(sizeof(ContextRecord->Eax) == 4); - - // If the exception is not a page fault, exit. - if (ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) - { - return EXCEPTION_CONTINUE_SEARCH; - } - - // get bad virtual address - addr = (u32)ExceptionRecord->ExceptionInformation[1]; - - if( (unsigned)(addr-(u32)PS2MEM_BASE) < 0x60000000) { - PSMEMORYMAP* pmap; - - pmap = &memLUT[(addr-(u32)PS2MEM_BASE)>>12]; - - if( !pmap->aPFNs ) { - // NOTE: this is a hack because the address is truncated and there's no way - // to tell what it's upper bits are (due to OS limitations). - pmap += 0x80000; - if( !pmap->aPFNs ) { - pmap += 0x20000; - if( !pmap->aPFNs ) goto OtherException; - } - //else addr += 0x20000000; - } - - { - //LPVOID pnewaddr; not used - uptr curvaddr = pmap->aVFNs[0]; - - if( curvaddr ) { - // delete the current mapping - SysMapUserPhysicalPages((void*)curvaddr, 1, NULL, 0); - } - - assert( pmap->aPFNs[0] != 0 ); - - pmap->aVFNs[0] = curvaddr = addr&~0xfff; - if( SysMapUserPhysicalPages((void*)curvaddr, 1, pmap->aPFNs, 0) ) - return EXCEPTION_CONTINUE_EXECUTION; - - // try allocing the virtual mem - //pnewaddr = <- not used - /* use here the size of allocation granularity and force rounding down, - because in reserve mode the address is rounded up/down to the nearest - multiple of this granularity; if you did it not this way, in some cases - the same address would be used twice, so the api fails */ - VirtualAlloc((void*)(curvaddr&~0xffff), 0x10000, MEM_RESERVE|MEM_PHYSICAL, PAGE_READWRITE); - - if( SysMapUserPhysicalPages((void*)curvaddr, 1, pmap->aPFNs, 0) ) - return EXCEPTION_CONTINUE_EXECUTION; - - Console::Error("Virtual Memory Error > page 0x%x cannot be found %d (p:%x,v:%x)", params - addr-(u32)PS2MEM_BASE, GetLastError(), pmap->aPFNs[0], curvaddr); - } - } - // check if vumem - else if( (addr&0xffff4000) == 0x11000000 ) { - // vu0mem - SysMapUserPhysicalPages((void*)s_psVuMem.aVFNs[1], 1, NULL, 0); - - s_psVuMem.aVFNs[1] = addr&~0xfff; - SysMapUserPhysicalPages((void*)addr, 1, s_psVuMem.aPFNs, 1); - - //SysPrintf("Exception: vumem\n"); - return EXCEPTION_CONTINUE_EXECUTION; - } -OtherException: - -#ifdef VM_HACK - { - u32 code = *(u32*)ExceptionRecord->ExceptionAddress; - u32 rd = 0; - - if( ExceptionRecord->ExceptionInformation[0] ) { - //SKIP_WRITE(); - // shouldn't be writing - } - else { - SysPrintf("vmhack "); - SKIP_READ(); - //((u32*)&ContextRecord->Eax)[rd] = 0; - return EXCEPTION_CONTINUE_EXECUTION; // TODO: verify this!!! - } - } -DefaultHandler: -#endif - - return EXCEPTION_CONTINUE_SEARCH; -} - -#else // linux implementation - -#define VIRTUAL_ALLOC(base, size, Protection) { \ - void* lpMemReserved = mmap( base, size, Protection, MAP_PRIVATE|MAP_ANONYMOUS ); \ - if( lpMemReserved == NULL || base != lpMemReserved ) \ -{ \ - SysPrintf("Cannot reserve memory at 0x%8.8x(%x).\n", base, lpMemReserved); \ - perror("err"); \ - goto eCleanupAndExit; \ -} \ -} \ - -#define VIRTUAL_FREE(ptr, size) munmap(ptr, size) - -uptr *memLUT = NULL; - -void memAlloc() -{ - int i; - LPVOID pExtraMem = NULL; - - // release the previous reserved mem - munmap(PS2MEM_BASE, 0x40000000); - - // allocate all virtual memory - PHYSICAL_ALLOC(PS2MEM_BASE, Ps2MemSize::Base, s_psM); - VIRTUAL_ALLOC(PS2MEM_ROM, Ps2MemSize::Rom, PROT_READ); - VIRTUAL_ALLOC(PS2MEM_ROM1, Ps2MemSize::Rom1, PROT_READ); - VIRTUAL_ALLOC(PS2MEM_ROM2, Ps2MemSize::Rom2, PROT_READ); - VIRTUAL_ALLOC(PS2MEM_EROM, Ps2MemSize::ERom, PROT_READ); - PHYSICAL_ALLOC(PS2MEM_SCRATCH, 0x00010000, s_psS); - PHYSICAL_ALLOC(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); - PHYSICAL_ALLOC(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); - PHYSICAL_ALLOC(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); - - VIRTUAL_ALLOC(PS2MEM_PSXHW, Ps2MemSize::IopHardware, PROT_READ|PROT_WRITE); - VIRTUAL_ALLOC(PS2MEM_PSXHW4, 0x00010000, PROT_NONE); - VIRTUAL_ALLOC(PS2MEM_GS, 0x00002000, PROT_READ|PROT_WRITE); - VIRTUAL_ALLOC(PS2MEM_DEV9, 0x00010000, PROT_NONE); - VIRTUAL_ALLOC(PS2MEM_SPU2, 0x00010000, PROT_NONE); - VIRTUAL_ALLOC(PS2MEM_SPU2_, 0x00010000, PROT_NONE); - - VIRTUAL_ALLOC(PS2MEM_B80, 0x00010000, PROT_READ|PROT_WRITE); - VIRTUAL_ALLOC(PS2MEM_BA0, 0x00010000, PROT_READ|PROT_WRITE); - - // special addrs mmap - VIRTUAL_ALLOC(PS2MEM_BASE+0x5fff0000, 0x10000, PROT_READ|PROT_WRITE); - -eCleanupAndExit: - memShutdown(); - return -1; -} - -void memShutdown() -{ - VIRTUAL_FREE(PS2MEM_BASE, 0x40000000); - VIRTUAL_FREE(PS2MEM_PSX, 0x00800000); - - PHYSICAL_FREE(PS2MEM_BASE, Ps2MemSize::Base, s_psM); - VIRTUAL_FREE(PS2MEM_ROM, Ps2MemSize::Rom); - VIRTUAL_FREE(PS2MEM_ROM1, Ps2MemSize::Rom1); - VIRTUAL_FREE(PS2MEM_ROM2, Ps2MemSize::Rom2); - VIRTUAL_FREE(PS2MEM_EROM, Ps2MemSize::ERom); - PHYSICAL_FREE(PS2MEM_SCRATCH, 0x00010000, s_psS); - PHYSICAL_FREE(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); - PHYSICAL_FREE(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); - PHYSICAL_FREE(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); - - VIRTUAL_FREE(PS2MEM_VU0MICRO, 0x00010000); // allocate for all VUs - - VIRTUAL_FREE(PS2MEM_PSXHW, Ps2MemSize::IopHardware); - VIRTUAL_FREE(PS2MEM_PSXHW4, 0x00010000); - VIRTUAL_FREE(PS2MEM_GS, 0x00002000); - VIRTUAL_FREE(PS2MEM_DEV9, 0x00010000); - VIRTUAL_FREE(PS2MEM_SPU2, 0x00010000); - VIRTUAL_FREE(PS2MEM_SPU2_, 0x00010000); - - VIRTUAL_FREE(PS2MEM_B80, 0x00010000); - VIRTUAL_FREE(PS2MEM_BA0, 0x00010000); - - VirtualFree(PS2MEM_VU0MICRO, 0, MEM_RELEASE); - - safe_aligned_free(memLUT); - - // reserve mem - if( mmap(PS2MEM_BASE, 0x40000000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) != PS2MEM_BASE ) { - SysPrintf("failed to reserve mem\n"); - } -} - -#endif // _WIN32 - -void vm_Reset() -{ - jASSUME( memLUT != NULL ); - - memzero_ptr<sizeof(PSMEMORYMAP)*0x100000>(memLUT); - for (int i=0; i<0x02000; i++) memLUT[i + 0x00000] = initMemoryMap(&s_psM.aPFNs[i], &s_psM.aVFNs[i]); - for (int i=2; i<0x00010; i++) memLUT[i + 0x10000] = initMemoryMap(&s_psHw.aPFNs[i], &s_psHw.aVFNs[i]); - for (int i=0; i<0x00800; i++) memLUT[i + 0x1c000] = initMemoryMap(&s_psxM.aPFNs[(i & 0x1ff)], &s_psxM.aVFNs[(i & 0x1ff)]); - for (int i=0; i<0x00004; i++) - { - memLUT[i + 0x11000] = initMemoryMap(&s_psVuMem.aPFNs[0], &s_psVuMem.aVFNs[0]); - memLUT[i + 0x11004] = initMemoryMap(&s_psVuMem.aPFNs[1], &s_psVuMem.aVFNs[1]); - memLUT[i + 0x11008] = initMemoryMap(&s_psVuMem.aPFNs[4+i], &s_psVuMem.aVFNs[4+i]); - memLUT[i + 0x1100c] = initMemoryMap(&s_psVuMem.aPFNs[8+i], &s_psVuMem.aVFNs[8+i]); - - // Yay! Scratchpad mapping! We love the scratchpad. - memLUT[i + 0x50000] = initMemoryMap(&s_psS.aPFNs[i], &s_psS.aVFNs[i]); - } - - // map to other modes - memcpy(memLUT+0x80000, memLUT, 0x20000*sizeof(PSMEMORYMAP)); - memcpy(memLUT+0xa0000, memLUT, 0x20000*sizeof(PSMEMORYMAP)); -} - -// Some games read/write between different addrs but same physical memory -// this causes major slowdowns because it goes into the exception handler, so use this (zerofrog) -u32 VM_RETRANSLATE(u32 mem) -{ - u8* p, *pbase; - if( (mem&0xffff0000) == 0x50000000 ) // reserved scratch pad mem - return PS2MEM_BASE_+mem; - - p = (u8*)dmaGetAddrBase(mem); - -#ifdef _WIN32 - // do manual LUT since IPU/SPR seems to use addrs 0x3000xxxx quite often - if( memLUT[ (p-PS2MEM_BASE)>>12 ].aPFNs == NULL ) { - return PS2MEM_BASE_+mem; - } - - pbase = (u8*)memLUT[ (p-PS2MEM_BASE)>>12 ].aVFNs[0]; - if( pbase != NULL ) - p = pbase + ((u32)p&0xfff); -#endif - - return (u32)p; -} - -void memSetPageAddr(u32 vaddr, u32 paddr) { - - PSMEMORYMAP* pmap; - - if( vaddr == paddr ) - return; - - if( (vaddr>>28) != 1 && (vaddr>>28) != 9 && (vaddr>>28) != 11 ) { -#ifdef _WIN32 - pmap = &memLUT[vaddr >> 12]; - - if( pmap->aPFNs != NULL && (pmap->aPFNs != memLUT[paddr>>12].aPFNs || - pmap->aVFNs[0] != TRANSFORM_ADDR(vaddr)+(u32)PS2MEM_BASE) ) { - - SysMapUserPhysicalPages((void*)pmap->aVFNs[0], 1, NULL, 0); - pmap->aVFNs[0] = 0; - } - - *pmap = memLUT[paddr >> 12]; -#else - memLUT[vaddr>>12] = memLUT[paddr>>12]; -#endif - } -} - -void memClearPageAddr(u32 vaddr) { - // SysPrintf("memClearPageAddr: %8.8x\n", vaddr); - - if ((vaddr & 0xffffc000) == 0x70000000) return; - -#ifdef _WIN32 - // if( vaddr >= 0x20000000 && vaddr < 0x80000000 ) { - // Cpu->Clear(vaddr&~0xfff, 0x1000/4); - // if( memLUT[vaddr>>12].aVFNs != NULL ) { - // SysMapUserPhysicalPages((void*)memLUT[vaddr>>12].aVFNs[0], 1, NULL, 0 ); - // memLUT[vaddr>>12].aVFNs = NULL; - // memLUT[vaddr>>12].aPFNs = NULL; - // } - // } -#else - if( memLUT[vaddr>>12] != NULL ) { - SysVirtualFree(memLUT[vaddr>>12], 0x1000); - memLUT[vaddr>>12] = 0; - } -#endif -} - -u8 recMemRead8() -{ - register u32 mem; - __asm mov mem, ecx // already anded with ~0xa0000000 - - switch( (mem&~0xffff) ) { -case 0x1f400000: return psxHw4Read8(mem); -case 0x10000000: return hwRead8(mem); -case 0x1f800000: return psxHwRead8(mem); -case 0x12000000: return *(PS2MEM_BASE+(mem&~0xc00)); -case 0x14000000: - { - u32 ret = DEV9read8(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, ret); - return ret; - } - -default: - return *(u8*)(PS2MEM_BASE+mem); - } - MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); - - cpuTlbMissR(mem, cpuRegs.branch); - - return 0; -} - -void _eeReadConstMem8(int mmreg, u32 mem, int sign) -{ - assert( !IS_XMMREG(mmreg)); - - if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVDMtoMMX(mmreg&0xf, mem-3); - assert(0); - } - else { - if( sign ) MOVSX32M8toR(mmreg, mem); - else MOVZX32M8toR(mmreg, mem); - } -} - -void _eeReadConstMem16(int mmreg, u32 mem, int sign) -{ - assert( !IS_XMMREG(mmreg)); - - if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVDMtoMMX(mmreg&0xf, mem-2); - assert(0); - } - else { - if( sign ) MOVSX32M16toR(mmreg, mem); - else MOVZX32M16toR(mmreg, mem); - } -} - -void _eeReadConstMem32(int mmreg, u32 mem) -{ - if( IS_XMMREG(mmreg) ) SSEX_MOVD_M32_to_XMM(mmreg&0xf, mem); - else if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVDMtoMMX(mmreg&0xf, mem); - } - else MOV32MtoR(mmreg, mem); -} - -void _eeReadConstMem128(int mmreg, u32 mem) -{ - if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVQMtoR((mmreg>>4)&0xf, mem+8); - MOVQMtoR(mmreg&0xf, mem); - } - else SSEX_MOVDQA_M128_to_XMM( mmreg&0xf, mem); -} - -void _eeWriteConstMem8(u32 mem, int mmreg) -{ - assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); - if( IS_EECONSTREG(mmreg) ) MOV8ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) MOV8ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else MOV8RtoM(mem, mmreg); -} - -void _eeWriteConstMem16(u32 mem, int mmreg) -{ - assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); - if( IS_EECONSTREG(mmreg) ) MOV16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) MOV16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else MOV16RtoM(mem, mmreg); -} - -// op - 0 for AND, 1 for OR -void _eeWriteConstMem16OP(u32 mem, int mmreg, int op) -{ - assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); - switch(op) { -case 0: // AND operation - if( IS_EECONSTREG(mmreg) ) AND16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) AND16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else AND16RtoM(mem, mmreg); - break; -case 1: // OR operation - if( IS_EECONSTREG(mmreg) ) OR16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) OR16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else OR16RtoM(mem, mmreg); - break; - - jNO_DEFAULT - } -} - -void _eeWriteConstMem32(u32 mem, int mmreg) -{ - if( IS_XMMREG(mmreg) ) SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); - else if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVDMMXtoM(mem, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) MOV32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else MOV32RtoM(mem, mmreg); -} - -void _eeWriteConstMem32OP(u32 mem, int mmreg, int op) -{ - switch(op) { -case 0: // and - if( IS_XMMREG(mmreg) ) { - _deleteEEreg((mmreg>>16)&0x1f, 1); - SSE2_PAND_M128_to_XMM(mmreg&0xf, mem); - SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); - } - else if( IS_MMXREG(mmreg) ) { - _deleteEEreg((mmreg>>16)&0x1f, 1); - SetMMXstate(); - PANDMtoR(mmreg&0xf, mem); - MOVDMMXtoM(mem, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) { - AND32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - } - else if( IS_PSXCONSTREG(mmreg) ) { - AND32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - } - else { - AND32RtoM(mem, mmreg&0xf); - } - break; - -case 1: // or - if( IS_XMMREG(mmreg) ) { - _deleteEEreg((mmreg>>16)&0x1f, 1); - SSE2_POR_M128_to_XMM(mmreg&0xf, mem); - SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); - } - else if( IS_MMXREG(mmreg) ) { - _deleteEEreg((mmreg>>16)&0x1f, 1); - SetMMXstate(); - PORMtoR(mmreg&0xf, mem); - MOVDMMXtoM(mem, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) { - OR32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - } - else if( IS_PSXCONSTREG(mmreg) ) { - OR32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); - } - else { - OR32RtoM(mem, mmreg&0xf); - } - break; - -case 2: // not and - if( mmreg & MEM_XMMTAG ) { - _deleteEEreg(mmreg>>16, 1); - SSEX_PANDN_M128_to_XMM(mmreg&0xf, mem); - SSEX_MOVD_XMM_to_M32(mem, mmreg&0xf); - } - else if( mmreg & MEM_MMXTAG ) { - _deleteEEreg(mmreg>>16, 1); - PANDNMtoR(mmreg&0xf, mem); - MOVDMMXtoM(mem, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) { - AND32ItoM(mem, ~g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - } - else if( IS_PSXCONSTREG(mmreg) ) { - AND32ItoM(mem, ~g_psxConstRegs[((mmreg>>16)&0x1f)]); - } - else { - NOT32R(mmreg&0xf); - AND32RtoM(mem, mmreg&0xf); - } - break; - -default: assert(0); - } -} - -void _eeWriteConstMem64(u32 mem, int mmreg) -{ - if( IS_XMMREG(mmreg) ) SSE_MOVLPS_XMM_to_M64(mem, mmreg&0xf); - else if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVQRtoM(mem, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) { - MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - MOV32ItoM(mem+4, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[1]); - } - else assert(0); -} - -void _eeWriteConstMem128(u32 mem, int mmreg) -{ - if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVQRtoM(mem, mmreg&0xf); - MOVQRtoM(mem+8, (mmreg>>4)&0xf); - } - else if( IS_EECONSTREG(mmreg) ) { - SetMMXstate(); - MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - MOV32ItoM(mem+4, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[1]); - MOVQRtoM(mem+8, mmreg&0xf); - } - else SSEX_MOVDQA_XMM_to_M128(mem, mmreg&0xf); -} - -void _eeMoveMMREGtoR(x86IntRegType to, int mmreg) -{ - if( IS_XMMREG(mmreg) ) SSE2_MOVD_XMM_to_R(to, mmreg&0xf); - else if( IS_MMXREG(mmreg) ) { - SetMMXstate(); - MOVD32MMXtoR(to, mmreg&0xf); - } - else if( IS_EECONSTREG(mmreg) ) MOV32ItoR(to, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); - else if( IS_PSXCONSTREG(mmreg) ) MOV32ItoR(to, g_psxConstRegs[((mmreg>>16)&0x1f)]); - else if( mmreg != to ) MOV32RtoR(to, mmreg); -} - -int recMemConstRead8(u32 x86reg, u32 mem, u32 sign) -{ - mem = TRANSFORM_ADDR(mem); - - switch( mem>>16 ) { -case 0x1f40: return psxHw4ConstRead8(x86reg, mem, sign); -case 0x1000: return hwConstRead8(x86reg, mem, sign); -case 0x1f80: return psxHwConstRead8(x86reg, mem, sign); -case 0x1200: return gsConstRead8(x86reg, mem, sign); -case 0x1400: - { - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9read8); - if( sign ) MOVSX32R8toR(EAX, EAX); - else MOVZX32R8toR(EAX, EAX); - return 1; - } - -default: - _eeReadConstMem8(x86reg, VM_RETRANSLATE(mem), sign); - return 0; - } -} - -u16 recMemRead16() { - - register u32 mem; - __asm mov mem, ecx // already anded with ~0xa0000000 - - switch( mem>>16 ) { - case 0x1000: return hwRead16(mem); - case 0x1f80: return psxHwRead16(mem); - case 0x1200: return *(u16*)(PS2MEM_BASE+(mem&~0xc00)); - case 0x1800: return 0; - case 0x1a00: return ba0R16(mem); - case 0x1f90: - case 0x1f00: - return SPU2read(mem); - case 0x1400: - { - u32 ret = DEV9read16(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, ret); - return ret; - } - - default: - return *(u16*)(PS2MEM_BASE+mem); - } - MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - return 0; -} - -int recMemConstRead16(u32 x86reg, u32 mem, u32 sign) -{ - mem = TRANSFORM_ADDR(mem); - - switch( mem>>16 ) { -case 0x1000: return hwConstRead16(x86reg, mem, sign); -case 0x1f80: return psxHwConstRead16(x86reg, mem, sign); -case 0x1200: return gsConstRead16(x86reg, mem, sign); -case 0x1800: - if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); - else XOR32RtoR(x86reg, x86reg); - return 0; -case 0x1a00: - iFlushCall(0); - PUSH32I(mem); - CALLFunc((u32)ba0R16); - ADD32ItoR(ESP, 4); - if( sign ) MOVSX32R16toR(EAX, EAX); - else MOVZX32R16toR(EAX, EAX); - return 1; - -case 0x1f90: -case 0x1f00: - iFlushCall(0); - PUSH32I(mem); - CALLFunc((u32)SPU2read); - if( sign ) MOVSX32R16toR(EAX, EAX); - else MOVZX32R16toR(EAX, EAX); - return 1; - -case 0x1400: - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9read16); - if( sign ) MOVSX32R16toR(EAX, EAX); - else MOVZX32R16toR(EAX, EAX); - return 1; - -default: - _eeReadConstMem16(x86reg, VM_RETRANSLATE(mem), sign); - return 0; - } -} - -__declspec(naked) -u32 recMemRead32() { - // ecx is address - already anded with ~0xa0000000 - __asm { - - mov edx, ecx - shr edx, 16 - cmp dx, 0x1000 - je hwread - cmp dx, 0x1f80 - je psxhwread - cmp dx, 0x1200 - je gsread - cmp dx, 0x1400 - je devread - - // default read - mov eax, dword ptr [ecx+PS2MEM_BASE_] - ret - } - -hwread: - { - __asm { - cmp ecx, 0x10002000 - jb counterread - - cmp ecx, 0x1000f260 - je hwsifpresetread - cmp ecx, 0x1000f240 - je hwsifsyncread - cmp ecx, 0x1000f440 - je hwmch_drd - cmp ecx, 0x1000f430 - je hwmch_ricm - - cmp ecx, 0x10003000 - jb hwdefread2 - mov eax, dword ptr [ecx+PS2MEM_BASE_] - ret - - // ipu -hwdefread2: - push ecx - call ipuRead32 - add esp, 4 - ret - - // sif -hwsifpresetread: - xor eax, eax - ret -hwsifsyncread: - mov eax, 0x1000F240 - mov eax, dword ptr [eax+PS2MEM_BASE_] - or eax, 0xF0000102 - ret - } - -counterread: - { - static u32 mem, index; - - // counters - __asm mov mem, ecx - index = (mem>>11)&3; - - if( (mem&0x7ff) == 0 ) { - __asm { - push index - call rcntRcount - add esp, 4 - and eax, 0xffff - ret - } - } - - index = (u32)&counters[index] + ((mem>>2)&0xc); - - __asm { - mov eax, index - mov eax, dword ptr [eax] - movzx eax, ax - ret - } - } - -hwmch_drd: // MCH_DRD - __asm { - mov eax, dword ptr [ecx+PS2MEM_BASE_-0x10] - shr eax, 6 - test eax, 0xf - jz mch_drd_2 -hwmch_ricm: - xor eax, eax - ret - -mch_drd_2: - shr eax, 10 - and eax, 0xfff - cmp eax, 0x21 // INIT - je mch_drd_init - cmp eax, 0x23 // CNFGA - je mch_drd_cnfga - cmp eax, 0x24 // CNFGB - je mch_drd_cnfgb - cmp eax, 0x40 // DEVID - je mch_drd_devid - xor eax, eax - ret - -mch_drd_init: - mov edx, rdram_devices - xor eax, eax - cmp edx, rdram_sdevid - setg al - add rdram_sdevid, eax - imul eax, 0x1f - ret - -mch_drd_cnfga: - mov eax, 0x0D0D - ret - -mch_drd_cnfgb: - mov eax, 0x0090 - ret - -mch_drd_devid: - mov eax, dword ptr [ecx+PS2MEM_BASE_-0x10] - and eax, 0x1f - ret - } - } - -psxhwread: - __asm { - push ecx - call psxHwRead32 - add esp, 4 - ret - } - -gsread: - __asm { - and ecx, 0xfffff3ff - mov eax, dword ptr [ecx+PS2MEM_BASE_] - ret - } - -devread: - __asm { - and ecx, 0xfbffffff - push ecx - call DEV9read32 - add esp, 4 - ret - } -} - -int recMemConstRead32(u32 x86reg, u32 mem) -{ - mem = TRANSFORM_ADDR(mem); - - switch( (mem&~0xffff) ) { -case 0x10000000: return hwConstRead32(x86reg, mem); -case 0x1f800000: return psxHwConstRead32(x86reg, mem); -case 0x12000000: return gsConstRead32(x86reg, mem); -case 0x14000000: - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9read32); - return 1; - -default: - _eeReadConstMem32(x86reg, VM_RETRANSLATE(mem)); - return 0; - } -} - -void recMemRead64(u64 *out) -{ - register u32 mem; - __asm mov mem, ecx // already anded with ~0xa0000000 - - switch( (mem&0xffff0000) ) { -case 0x10000000: *out = hwRead64(mem); return; -case 0x11000000: *out = *(u64*)(PS2MEM_BASE+mem); return; -case 0x12000000: *out = *(u64*)(PS2MEM_BASE+(mem&~0xc00)); return; - -default: - //assert(0); - *out = *(u64*)(PS2MEM_BASE+mem); - return; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); -} - -void recMemConstRead64(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( (mem&0xffff0000) ) { -case 0x10000000: hwConstRead64(mem, mmreg); return; -case 0x12000000: gsConstRead64(mem, mmreg); return; -default: - if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, VM_RETRANSLATE(mem)); - else { - MOVQMtoR(mmreg, VM_RETRANSLATE(mem)); - SetMMXstate(); - } - return; - } -} - -void recMemRead128(u64 *out) { - - register u32 mem; - __asm mov mem, ecx // already anded with ~0xa0000000 - - switch( (mem&0xffff0000) ) { - case 0x10000000: - hwRead128(mem, out); - return; - case 0x12000000: - out[0] = *(u64*)(PS2MEM_BASE+(mem&~0xc00)); - out[1] = *(u64*)(PS2MEM_BASE+(mem&~0xc00)+8); - return; - case 0x11000000: - out[0] = *(u64*)(PS2MEM_BASE+mem); - out[1] = *(u64*)(PS2MEM_BASE+mem+8); - return; - default: - //assert(0); - out[0] = *(u64*)(PS2MEM_BASE+mem); - out[1] = *(u64*)(PS2MEM_BASE+mem+8); - return; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); -} - -void recMemConstRead128(u32 mem, int xmmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( (mem&0xffff0000) ) { -case 0x10000000: hwConstRead128(mem, xmmreg); return; -case 0x12000000: gsConstRead128(mem, xmmreg); return; -default: - _eeReadConstMem128(xmmreg, VM_RETRANSLATE(mem)); - return; - } -} - -void errwrite() -{ - int i, bit, tempeax; - __asm mov i, ecx - __asm mov tempeax, eax - __asm mov bit, edx - SysPrintf("Error write%d at %x\n", bit, i); - assert(0); - __asm mov eax, tempeax - __asm mov ecx, i -} - -void recMemWrite8() -{ - register u32 mem; - register u8 value; - __asm mov mem, ecx // already anded with ~0xa0000000 - __asm mov value, al - - switch( mem>>16 ) { -case 0x1f40: psxHw4Write8(mem, value); return; -case 0x1000: hwWrite8(mem, value); return; -case 0x1f80: psxHwWrite8(mem, value); return; -case 0x1200: gsWrite8(mem, value); return; -case 0x1400: - DEV9write8(mem & ~0x04000000, value); - SysPrintf("DEV9 write8 %8.8lx: %2.2lx\n", mem & ~0x04000000, value); - return; - -#ifdef _DEBUG -case 0x1100: assert(0); -#endif -default: - // vus, bad addrs, etc - *(u8*)(PS2MEM_BASE+mem) = value; - return; - } - MEM_LOG("Unknown Memory write8 to address %x with data %2.2x\n", mem, value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -int recMemConstWrite8(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( mem>>16 ) { -case 0x1f40: psxHw4ConstWrite8(mem, mmreg); return 0; -case 0x1000: hwConstWrite8(mem, mmreg); return 0; -case 0x1f80: psxHwConstWrite8(mem, mmreg); return 0; -case 0x1200: gsConstWrite8(mem, mmreg); return 0; -case 0x1400: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9write8); - return 0; - -case 0x1100: - _eeWriteConstMem8(PS2MEM_BASE_+mem, mmreg); - - if( mem < 0x11004000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU0->Clear); - ADD32ItoR(ESP, 8); - } - else if( mem >= 0x11008000 && mem < 0x1100c000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU1->Clear); - ADD32ItoR(ESP, 8); - } - return 0; - -default: - _eeWriteConstMem8(PS2MEM_BASE_+mem, mmreg); - return 1; - } -} - -void recMemWrite16() { - - register u32 mem; - register u16 value; - __asm mov mem, ecx // already anded with ~0xa0000000 - __asm mov value, ax - - switch( mem>>16 ) { - case 0x1000: hwWrite16(mem, value); return; - case 0x1600: - //HACK: DEV9 VM crash fix - return; - case 0x1f80: psxHwWrite16(mem, value); return; - case 0x1200: gsWrite16(mem, value); return; - case 0x1f90: - case 0x1f00: SPU2write(mem, value); return; - case 0x1400: - DEV9write16(mem & ~0x04000000, value); - SysPrintf("DEV9 write16 %8.8lx: %4.4lx\n", mem & ~0x04000000, value); - return; - -#ifdef _DEBUG - case 0x1100: assert(0); -#endif - default: - // vus, bad addrs, etc - *(u16*)(PS2MEM_BASE+mem) = value; - return; - } - MEM_LOG("Unknown Memory write16 to address %x with data %4.4x\n", mem, value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -int recMemConstWrite16(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( mem>>16 ) { -case 0x1000: hwConstWrite16(mem, mmreg); return 0; -case 0x1600: - //HACK: DEV9 VM crash fix - return 0; -case 0x1f80: psxHwConstWrite16(mem, mmreg); return 0; -case 0x1200: gsConstWrite16(mem, mmreg); return 0; -case 0x1f90: -case 0x1f00: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((u32)SPU2write); - return 0; -case 0x1400: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9write16); - return 0; - -case 0x1100: - _eeWriteConstMem16(PS2MEM_BASE_+mem, mmreg); - - if( mem < 0x11004000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU0->Clear); - ADD32ItoR(ESP, 8); - } - else if( mem >= 0x11008000 && mem < 0x1100c000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU1->Clear); - ADD32ItoR(ESP, 8); - } - return 0; - -default: - _eeWriteConstMem16(PS2MEM_BASE_+mem, mmreg); - return 1; - } -} - -C_ASSERT( sizeof(BASEBLOCK) == 8 ); - -__declspec(naked) -void recMemWrite32() -{ - // ecx is address - already anded with ~0xa0000000 - __asm { - - mov edx, ecx - shr edx, 16 - cmp dx, 0x1000 - je hwwrite - cmp dx, 0x1f80 - je psxwrite - cmp dx, 0x1200 - je gswrite - cmp dx, 0x1400 - je devwrite - cmp dx, 0x1100 - je vuwrite - } - - __asm { - // default write - mov dword ptr [ecx+PS2MEM_BASE_], eax - ret - -hwwrite: - push eax - push ecx - call hwWrite32 - add esp, 8 - ret -psxwrite: - push eax - push ecx - call psxHwWrite32 - add esp, 8 - ret -gswrite: - push eax - push ecx - call gsWrite32 - add esp, 8 - ret -devwrite: - and ecx, 0xfbffffff - push eax - push ecx - call DEV9write32 - add esp, 8 - ret -vuwrite: - // default write - mov dword ptr [ecx+PS2MEM_BASE_], eax - - cmp ecx, 0x11004000 - jge vu1write - and ecx, 0x3ff8 - // clear vu0mem - mov eax, CpuVU0 - push 1 - push ecx - call [eax]CpuVU0.Clear - add esp, 8 - ret - -vu1write: - cmp ecx, 0x11008000 - jl vuend - cmp ecx, 0x1100c000 - jge vuend - // clear vu1mem - and ecx, 0x3ff8 - mov eax, CpuVU1 - push 1 - push ecx - call [eax]CpuVU1.Clear - add esp, 8 -vuend: - ret - } -} - -int recMemConstWrite32(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( mem&0xffff0000 ) { -case 0x10000000: hwConstWrite32(mem, mmreg); return 0; -case 0x1f800000: psxHwConstWrite32(mem, mmreg); return 0; -case 0x12000000: gsConstWrite32(mem, mmreg); return 0; -case 0x1f900000: -case 0x1f000000: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((u32)SPU2write); - return 0; -case 0x14000000: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem & ~0x04000000); - CALLFunc((u32)DEV9write32); - return 0; - -case 0x11000000: - _eeWriteConstMem32(PS2MEM_BASE_+mem, mmreg); - - if( mem < 0x11004000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU0->Clear); - ADD32ItoR(ESP, 8); - } - else if( mem >= 0x11008000 && mem < 0x1100c000 ) { - PUSH32I(1); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU1->Clear); - ADD32ItoR(ESP, 8); - } - return 0; - -default: - _eeWriteConstMem32(PS2MEM_BASE_+mem, mmreg); - return 1; - } -} - -__declspec(naked) void recMemWrite64() -{ - __asm { - mov edx, ecx - shr edx, 16 - cmp dx, 0x1000 - je hwwrite - cmp dx, 0x1200 - je gswrite - cmp dx, 0x1100 - je vuwrite - } - - __asm { - // default write - mov edx, 64 - call errwrite - -hwwrite: - push dword ptr [eax+4] - push dword ptr [eax] - push ecx - call hwWrite64 - add esp, 12 - ret - -gswrite: - push dword ptr [eax+4] - push dword ptr [eax] - push ecx - call gsWrite64 - add esp, 12 - ret - -vuwrite: - mov ebx, dword ptr [eax] - mov edx, dword ptr [eax+4] - mov dword ptr [ecx+PS2MEM_BASE_], ebx - mov dword ptr [ecx+PS2MEM_BASE_+4], edx - - cmp ecx, 0x11004000 - jge vu1write - and ecx, 0x3ff8 - // clear vu0mem - mov eax, CpuVU0 - push 2 - push ecx - call [eax]CpuVU0.Clear - add esp, 8 - ret - -vu1write: - cmp ecx, 0x11008000 - jl vuend - cmp ecx, 0x1100c000 - jge vuend - // clear vu1mem - and ecx, 0x3ff8 - mov eax, CpuVU0 - push 2 - push ecx - call [eax]CpuVU1.Clear - add esp, 8 -vuend: - ret - } -} - -int recMemConstWrite64(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( (mem>>16) ) { -case 0x1000: hwConstWrite64(mem, mmreg); return 0; -case 0x1200: gsConstWrite64(mem, mmreg); return 0; - -case 0x1100: - _eeWriteConstMem64(PS2MEM_BASE_+mem, mmreg); - - if( mem < 0x11004000 ) { - PUSH32I(2); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU0->Clear); - ADD32ItoR(ESP, 8); - } - else if( mem >= 0x11008000 && mem < 0x1100c000 ) { - PUSH32I(2); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU1->Clear); - ADD32ItoR(ESP, 8); - } - return 0; - -default: - _eeWriteConstMem64(PS2MEM_BASE_+mem, mmreg); - return 1; - } -} - -__declspec(naked) -void recMemWrite128() -{ - __asm { - - mov edx, ecx - shr edx, 16 - cmp dx, 0x1000 - je hwwrite - cmp dx, 0x1200 - je gswrite - cmp dx, 0x1100 - je vuwrite - } - - __asm { - mov edx, 128 - call errwrite - -hwwrite: - - push eax - push ecx - call hwWrite128 - add esp, 8 - ret - -vuwrite: - mov ebx, dword ptr [eax] - mov edx, dword ptr [eax+4] - mov edi, dword ptr [eax+8] - mov eax, dword ptr [eax+12] - mov dword ptr [ecx+PS2MEM_BASE_], ebx - mov dword ptr [ecx+PS2MEM_BASE_+4], edx - mov dword ptr [ecx+PS2MEM_BASE_+8], edi - mov dword ptr [ecx+PS2MEM_BASE_+12], eax - - cmp ecx, 0x11004000 - jge vu1write - and ecx, 0x3ff8 - // clear vu0mem - mov eax, CpuVU0 - push 4 - push ecx - call [eax]CpuVU0.Clear - add esp, 8 - ret - -vu1write: - cmp ecx, 0x11008000 - jl vuend - cmp ecx, 0x1100c000 - jge vuend - // clear vu1mem - and ecx, 0x3ff8 - mov eax, CpuVU1 - push 4 - push ecx - call [eax]CpuVU1.Clear - add esp, 8 -vuend: - - // default write - //movaps xmm7, qword ptr [eax] - - // removes possible exceptions and saves on remapping memory - // *might* be faster for certain games, no way to tell - // cmp ecx, 0x20000000 - // jb Write128 - // - // // look for better mapping - // mov edx, ecx - // shr edx, 12 - // shl edx, 3 - // add edx, memLUT - // mov edx, dword ptr [edx + 4] - // cmp edx, 0 - // je Write128 - // mov edx, dword ptr [edx] - // cmp edx, 0 - // je Write128 - // and ecx, 0xfff - // movaps qword ptr [ecx+edx], xmm7 - // jmp CheckOverwrite - //Write128: - //movaps qword ptr [ecx+PS2MEM_BASE_], xmm7 - ret - -gswrite: - sub esp, 8 - movlps xmm7, qword ptr [eax] - movlps qword ptr [esp], xmm7 - push ecx - call gsWrite64 - - // call again for upper 8 bytes - movlps xmm7, qword ptr [eax+8] - movlps qword ptr [esp+4], xmm7 - add [esp], 8 - call gsWrite64 - add esp, 12 - ret - } -} - -int recMemConstWrite128(u32 mem, int mmreg) -{ - mem = TRANSFORM_ADDR(mem); - - switch( (mem&0xffff0000) ) { -case 0x10000000: hwConstWrite128(mem, mmreg); return 0; -case 0x12000000: gsConstWrite128(mem, mmreg); return 0; - -case 0x11000000: - _eeWriteConstMem128(PS2MEM_BASE_+mem, mmreg); - - if( mem < 0x11004000 ) { - PUSH32I(4); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU0->Clear); - ADD32ItoR(ESP, 8); - } - else if( mem >= 0x11008000 && mem < 0x1100c000 ) { - PUSH32I(4); - PUSH32I(mem&0x3ff8); - CALLFunc((u32)CpuVU1->Clear); - ADD32ItoR(ESP, 8); - } - return 0; - -default: - _eeWriteConstMem128(PS2MEM_BASE_+mem, mmreg); - return 1; - } -} - -int __fastcall memRead8 (u32 mem, u8 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x1f400000: *out = psxHw4Read8(mem); return 0; - case 0x10000000: *out = hwRead8(mem); return 0; - case 0x1f800000: *out = psxHwRead8(mem); return 0; - case 0x12000000: *out = gsRead8(mem); return 0; - case 0x14000000: - *out = DEV9read8(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - - default: - *out = *(u8*)(PS2MEM_BASE+mem); - return 0; - } - -#ifdef MEM_LOG - MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); -#endif - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead8RS (u32 mem, u64 *out) -{ - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { -case 0x1f400000: *out = (s8)psxHw4Read8(mem); return 0; -case 0x10000000: *out = (s8)hwRead8(mem); return 0; -case 0x1f800000: *out = (s8)psxHwRead8(mem); return 0; -case 0x12000000: *out = (s8)gsRead8(mem); return 0; -case 0x14000000: - *out = (s8)DEV9read8(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - -default: - *out = *(s8*)(PS2MEM_BASE+mem); - return 0; - } - MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead8RU (u32 mem, u64 *out) -{ - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { -case 0x1f400000: *out = (u8)psxHw4Read8(mem); return 0; -case 0x10000000: *out = (u8)hwRead8(mem); return 0; -case 0x1f800000: *out = (u8)psxHwRead8(mem); return 0; -case 0x12000000: *out = (u8)gsRead8(mem); return 0; -case 0x14000000: - *out = (u8)DEV9read8(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - -default: - *out = *(u8*)(PS2MEM_BASE+mem); - return 0; - } - MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead16(u32 mem, u16 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: *out = hwRead16(mem); return 0; - case 0x1f800000: *out = psxHwRead16(mem); return 0; - case 0x12000000: *out = gsRead16(mem); return 0; - case 0x18000000: *out = 0; return 0; - case 0x1a000000: *out = ba0R16(mem); return 0; - case 0x1f900000: - case 0x1f000000: - *out = SPU2read(mem); return 0; - break; - case 0x14000000: - *out = DEV9read16(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - - default: - *out = *(u16*)(PS2MEM_BASE+mem); - return 0; - } - MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - return -1; -} - -int __fastcall memRead16RS(u32 mem, u64 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: *out = (s16)hwRead16(mem); return 0; - case 0x1f800000: *out = (s16)psxHwRead16(mem); return 0; - case 0x12000000: *out = (s16)gsRead16(mem); return 0; - case 0x18000000: *out = 0; return 0; - case 0x1a000000: *out = (s16)ba0R16(mem); return 0; - case 0x1f900000: - case 0x1f000000: - *out = (s16)SPU2read(mem); return 0; - break; - case 0x14000000: - *out = (s16)DEV9read16(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - - default: - *out = *(s16*)(PS2MEM_BASE+mem); - return 0; - } - MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - return -1; -} - -int __fastcall memRead16RU(u32 mem, u64 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: *out = (u16)hwRead16(mem ); return 0; - case 0x1f800000: *out = (u16)psxHwRead16(mem ); return 0; - case 0x12000000: *out = (u16)gsRead16(mem); return 0; - case 0x18000000: *out = 0; return 0; - case 0x1a000000: *out = (u16)ba0R16(mem); return 0; - case 0x1f900000: - case 0x1f000000: - *out = (u16)SPU2read(mem ); return 0; - break; - case 0x14000000: - *out = (u16)DEV9read16(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - - default: - *out = *(u16*)(PS2MEM_BASE+mem); - return 0; - } - MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); - cpuTlbMissR(mem, cpuRegs.branch); - return -1; -} - -int __fastcall memRead32(u32 mem, u32 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: *out = hwRead32(mem); return 0; - case 0x1f800000: *out = psxHwRead32(mem); return 0; - case 0x12000000: *out = gsRead32(mem); return 0; - case 0x14000000: - *out = (u32)DEV9read32(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - - default: - *out = *(u32*)(PS2MEM_BASE+mem); - return 0; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead32RS(u32 mem, u64 *out) -{ - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { -case 0x10000000: *out = (s32)hwRead32(mem); return 0; -case 0x1f800000: *out = (s32)psxHwRead32(mem); return 0; -case 0x12000000: *out = (s32)gsRead32(mem); return 0; -case 0x14000000: - *out = (s32)DEV9read32(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - -default: - *out = *(s32*)(PS2MEM_BASE+mem); - return 0; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead32RU(u32 mem, u64 *out) -{ - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { -case 0x10000000: *out = (u32)hwRead32(mem); return 0; -case 0x1f800000: *out = (u32)psxHwRead32(mem); return 0; -case 0x12000000: *out = (u32)gsRead32(mem); return 0; -case 0x14000000: - *out = (u32)DEV9read32(mem & ~0x04000000); - SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); - return 0; - -default: - *out = *(u32*)(PS2MEM_BASE+mem); - return 0; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead64(u32 mem, u64 *out) { - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: *out = hwRead64(mem); return 0; - case 0x12000000: *out = gsRead64(mem); return 0; - - default: - *out = *(u64*)(PS2MEM_BASE+mem); - return 0; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -int __fastcall memRead128(u32 mem, u64 *out) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: - hwRead128(mem, out); - return 0; - case 0x12000000: - out[0] = gsRead64(mem); - out[1] = gsRead64(mem + 8); - return 0; - - default: - out[0] = *(u64*)(PS2MEM_BASE+mem); - out[1] = *(u64*)(PS2MEM_BASE+mem+8); - return 0; - } - - MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); - cpuTlbMissR(mem, cpuRegs.branch); - - return -1; -} - -void __fastcall memWrite8 (u32 mem, u8 value) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x1f400000: psxHw4Write8(mem, value); return; - case 0x10000000: hwWrite8(mem, value); return; - case 0x1f800000: psxHwWrite8(mem, value); return; - case 0x12000000: gsWrite8(mem, value); return; - case 0x14000000: - DEV9write8(mem & ~0x04000000, value); - SysPrintf("DEV9 write8 %8.8lx: %2.2lx\n", mem & ~0x04000000, value); - return; - - default: - *(u8*)(PS2MEM_BASE+mem) = value; - - if (CHECK_EEREC) { - REC_CLEARM(mem&~3); - } - return; - } - MEM_LOG("Unknown Memory write8 to address %x with data %2.2x\n", mem, value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -void __fastcall memWrite16(u32 mem, u16 value) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: hwWrite16(mem, value); return; - case 0x1f800000: psxHwWrite16(mem, value); return; - case 0x12000000: gsWrite16(mem, value); return; - case 0x1f900000: - case 0x1f000000: SPU2write(mem, value); return; - case 0x14000000: - DEV9write16(mem & ~0x04000000, value); - SysPrintf("DEV9 write16 %8.8lx: %4.4lx\n", mem & ~0x04000000, value); - return; - - default: - *(u16*)(PS2MEM_BASE+mem) = value; - if (CHECK_EEREC) { - REC_CLEARM(mem&~3); - } - return; - } - MEM_LOG("Unknown Memory write16 to address %x with data %4.4x\n", mem, value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -void __fastcall memWrite32(u32 mem, u32 value) -{ - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { -case 0x10000000: hwWrite32(mem, value); return; -case 0x1f800000: psxHwWrite32(mem, value); return; -case 0x12000000: gsWrite32(mem, value); return; -case 0x1f900000: -case 0x1f000000: SPU2write(mem, value); return; -case 0x14000000: - DEV9write32(mem & ~0x4000000, value); - SysPrintf("DEV9 write32 %8.8lx: %8.8lx\n", mem & ~0x4000000, value); - return; - -default: - *(u32*)(PS2MEM_BASE+mem) = value; - - if (CHECK_EEREC) { - REC_CLEARM(mem); - } - return; - } - MEM_LOG("Unknown Memory write32 to address %x with data %8.8x\n", mem, value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -void __fastcall memWrite64(u32 mem, const u64* value) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: hwWrite64(mem, *value); return; - case 0x12000000: gsWrite64(mem, *value); return; - - default: - *(u64*)(PS2MEM_BASE+mem) = *value; - - if (CHECK_EEREC) { - REC_CLEARM(mem); - REC_CLEARM(mem+4); - } - return; - } - MEM_LOG("Unknown Memory write64 to address %x with data %8.8x_%8.8x\n", mem, (u32)((*value)>>32), (u32)value); - cpuTlbMissW(mem, cpuRegs.branch); -} - -void __fastcall memWrite128(u32 mem, const u64 *value) { - - mem = TRANSFORM_ADDR(mem); - switch( (mem&~0xffff) ) { - case 0x10000000: hwWrite128(mem, value); return; - case 0x12000000: - gsWrite64(mem, value[0]); - gsWrite64(mem + 8, value[1]); - return; - - default: - *(u64*)(PS2MEM_BASE+mem) = value[0]; - *(u64*)(PS2MEM_BASE+mem+8) = value[1]; - - if (CHECK_EEREC) { - REC_CLEARM(mem); - REC_CLEARM(mem+4); - REC_CLEARM(mem+8); - REC_CLEARM(mem+12); - } - return; - } - MEM_LOG("Unknown Memory write128 to address %x with data %8.8x_%8.8x_%8.8x_%8.8x\n", mem, ((u32*)value)[3], ((u32*)value)[2], ((u32*)value)[1], ((u32*)value)[0]); - cpuTlbMissW(mem, cpuRegs.branch); -} - -// Resets memory mappings, unmaps TLBs, reloads bios roms, etc. -void memReset() -{ -#ifdef _WIN32 - DWORD OldProtect; - // make sure can write - VirtualProtect(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READWRITE, &OldProtect); - VirtualProtect(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READWRITE, &OldProtect); - VirtualProtect(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READWRITE, &OldProtect); - VirtualProtect(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READWRITE, &OldProtect); -#else - mprotect(PS2EMEM_ROM, Ps2MemSize::Rom, PROT_READ|PROT_WRITE); - mprotect(PS2EMEM_ROM1, Ps2MemSize::Rom1, PROT_READ|PROT_WRITE); - mprotect(PS2EMEM_ROM2, Ps2MemSize::Rom2, PROT_READ|PROT_WRITE); - mprotect(PS2EMEM_EROM, Ps2MemSize::ERom, PROT_READ|PROT_WRITE); -#endif - - memzero_ptr<Ps2MemSize::Base>(PS2MEM_BASE); - memzero_ptr<Ps2MemSize::Scratch>(PS2MEM_SCRATCH); - vm_Reset(); - - string Bios; - FILE *fp; - - Path::Combine( Bios, Config.BiosDir, Config.Bios ); - - long filesize; - if( ( filesize = Path::getFileSize( Bios ) ) <= 0 ) - { - //Console::Error("Unable to load bios: '%s', PCSX2 can't run without that", params Bios); - throw Exception::FileNotFound( Bios, - "The specified Bios file was not found. A bios is required for Pcsx2 to run.\n\nFile not found" ); - } - - fp = fopen(Bios.c_str(), "rb"); - fread(PS2MEM_ROM, 1, std::min( (long)Ps2MemSize::Rom, filesize ), fp); - fclose(fp); - - BiosVersion = GetBiosVersion(); - Console::Status("Bios Version %d.%d", params BiosVersion >> 8, BiosVersion & 0xff); - - //injectIRX("host.irx"); //not fully tested; still buggy - - loadBiosRom("rom1", PS2MEM_ROM1, Ps2MemSize::Rom1); - loadBiosRom("rom2", PS2MEM_ROM2, Ps2MemSize::Rom2); - loadBiosRom("erom", PS2MEM_EROM, Ps2MemSize::ERom); - -#ifdef _WIN32 - VirtualProtect(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READONLY, &OldProtect); - VirtualProtect(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READONLY, &OldProtect); - VirtualProtect(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READONLY, &OldProtect); - VirtualProtect(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READONLY, &OldProtect); -#else - mprotect(PS2EMEM_ROM, Ps2MemSize::Rom, PROT_READ); - mprotect(PS2EMEM_ROM1, Ps2MemSize::Rom1, PROT_READ); - mprotect(PS2EMEM_ROM2, Ps2MemSize::Rom2, PROT_READ); - mprotect(PS2EMEM_EROM, Ps2MemSize::ERom, PROT_READ); -#endif -} - -#endif diff --git a/pcsx2/SPR.cpp b/pcsx2/SPR.cpp index 28d7225d89..d346f33c0e 100644 --- a/pcsx2/SPR.cpp +++ b/pcsx2/SPR.cpp @@ -81,7 +81,7 @@ int _SPR0chain() { mfifotransferred += spr0->qwc; } else { memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4); - Cpu->Clear(spr0->madr, spr0->qwc<<2); + //Cpu->Clear(spr0->madr, spr0->qwc<<2); // clear VU mem also! TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes) @@ -117,7 +117,7 @@ void _SPR0interleave() { hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4); mfifotransferred += spr0->qwc; } else { - Cpu->Clear(spr0->madr, spr0->qwc<<2); + //Cpu->Clear(spr0->madr, spr0->qwc<<2); // clear VU mem also! TestClearVUs(spr0->madr, spr0->qwc<<2); memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4); diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 573a33c793..2773ac336e 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -757,10 +757,6 @@ /> </FileConfiguration> </File> - <File - RelativePath="..\WinVM.cpp" - > - </File> <Filter Name="Debugger" > @@ -911,6 +907,14 @@ <Filter Name="Misc" > + <File + RelativePath="..\..\HashMap.h" + > + </File> + <File + RelativePath="..\..\HashTools.cpp" + > + </File> <File RelativePath="..\..\Misc.cpp" > @@ -2049,10 +2053,6 @@ RelativePath="..\..\x86\iR5900Branch.h" > </File> - <File - RelativePath="..\..\x86\iR5900CoissuedLoadStore.cpp" - > - </File> <File RelativePath="..\..\x86\iR5900Jump.h" > @@ -2229,34 +2229,6 @@ <Filter Name="Dynarec" > - <File - RelativePath="..\..\x86\iPsxMem.cpp" - > - <FileConfiguration - Name="Debug|Win32" - ExcludedFromBuild="true" - > - <Tool - Name="VCCLCompilerTool" - /> - </FileConfiguration> - <FileConfiguration - Name="Devel|Win32" - ExcludedFromBuild="true" - > - <Tool - Name="VCCLCompilerTool" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - ExcludedFromBuild="true" - > - <Tool - Name="VCCLCompilerTool" - /> - </FileConfiguration> - </File> <File RelativePath="..\..\x86\iR3000A.cpp" > @@ -2382,10 +2354,6 @@ RelativePath="..\..\Memory.h" > </File> - <File - RelativePath="..\..\MemoryVM.cpp" - > - </File> <File RelativePath="..\..\x86\ix86-32\recVTLB.cpp" > @@ -3106,10 +3074,6 @@ RelativePath="..\..\Stats.h" > </File> - <File - RelativePath="..\WinDebugResource" - > - </File> </Files> <Globals> </Globals> diff --git a/pcsx2/windows/WinVM.cpp b/pcsx2/windows/WinVM.cpp deleted file mode 100644 index f2ee585ed4..0000000000 --- a/pcsx2/windows/WinVM.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator - * Copyright (C) 2002-2009 Pcsx2 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 "PrecompiledHeader.h" -#include "win32.h" - - -#ifdef PCSX2_VIRTUAL_MEM - -// virtual memory/privileges -#include "ntsecapi.h" - -static wchar_t s_szUserName[255]; - -LRESULT WINAPI UserNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch(uMsg) { - case WM_INITDIALOG: - SetWindowPos(hDlg, HWND_TOPMOST, 200, 100, 0, 0, SWP_NOSIZE); - return TRUE; - - case WM_COMMAND: - switch(wParam) { - case IDOK: - { - wchar_t str[255]; - GetWindowTextW(GetDlgItem(hDlg, IDC_USER_NAME), str, 255); - swprintf(s_szUserName, 255, L"%hs", &str); - EndDialog(hDlg, TRUE ); - return TRUE; - } - - case IDCANCEL: - EndDialog(hDlg, FALSE ); - return TRUE; - } - break; - } - return FALSE; -} - -BOOL InitLsaString( - PLSA_UNICODE_STRING pLsaString, - LPCWSTR pwszString -) -{ - DWORD dwLen = 0; - - if (NULL == pLsaString) - return FALSE; - - if (NULL != pwszString) - { - dwLen = wcslen(pwszString); - if (dwLen > 0x7ffe) // String is too large - return FALSE; - } - - // Store the string. - pLsaString->Buffer = (WCHAR *)pwszString; - pLsaString->Length = (USHORT)dwLen * sizeof(WCHAR); - pLsaString->MaximumLength= (USHORT)(dwLen+1) * sizeof(WCHAR); - - return TRUE; -} - -PLSA_TRANSLATED_SID2 GetSIDInformation (LPWSTR AccountName,LSA_HANDLE PolicyHandle) -{ - LSA_UNICODE_STRING lucName; - PLSA_TRANSLATED_SID2 ltsTranslatedSID; - PLSA_REFERENCED_DOMAIN_LIST lrdlDomainList; - //LSA_TRUST_INFORMATION myDomain; - NTSTATUS ntsResult; - PWCHAR DomainString = NULL; - - // Initialize an LSA_UNICODE_STRING with the name. - if (!InitLsaString(&lucName, AccountName)) - { - wprintf(L"Failed InitLsaString\n"); - return NULL; - } - - ntsResult = LsaLookupNames2( - PolicyHandle, // handle to a Policy object - 0, - 1, // number of names to look up - &lucName, // pointer to an array of names - &lrdlDomainList, // receives domain information - <sTranslatedSID // receives relative SIDs - ); - if (0 != ntsResult) - { - wprintf(L"Failed LsaLookupNames - %lu \n", - LsaNtStatusToWinError(ntsResult)); - return NULL; - } - - // Get the domain the account resides in. -// myDomain = lrdlDomainList->Domains[ltsTranslatedSID->DomainIndex]; -// DomainString = (PWCHAR) LocalAlloc(LPTR, myDomain.Name.Length + 1); -// wcsncpy(DomainString, myDomain.Name.Buffer, myDomain.Name.Length); - - // Display the relative Id. -// wprintf(L"Relative Id is %lu in domain %ws.\n", -// ltsTranslatedSID->RelativeId, -// DomainString); - - LsaFreeMemory(lrdlDomainList); - - return ltsTranslatedSID; -} - -BOOL AddPrivileges(PSID AccountSID, LSA_HANDLE PolicyHandle, BOOL bAdd) -{ - LSA_UNICODE_STRING lucPrivilege; - NTSTATUS ntsResult; - - // Create an LSA_UNICODE_STRING for the privilege name(s). - if (!InitLsaString(&lucPrivilege, L"SeLockMemoryPrivilege")) - { - wprintf(L"Failed InitLsaString\n"); - return FALSE; - } - - if( bAdd ) { - ntsResult = LsaAddAccountRights( - PolicyHandle, // An open policy handle. - AccountSID, // The target SID. - &lucPrivilege, // The privilege(s). - 1 // Number of privileges. - ); - } - else { - ntsResult = LsaRemoveAccountRights( - PolicyHandle, // An open policy handle. - AccountSID, // The target SID - FALSE, - &lucPrivilege, // The privilege(s). - 1 // Number of privileges. - ); - } - - if (ntsResult == 0) - { - wprintf(L"Privilege added.\n"); - } - else - { - int err = LsaNtStatusToWinError(ntsResult); - char str[255]; - _snprintf(str, 255, "Privilege was not added - %lu \n", LsaNtStatusToWinError(ntsResult)); - MessageBox(NULL, str, "Privilege error", MB_OK); - return FALSE; - } - - return TRUE; -} - -#define TARGET_SYSTEM_NAME L"mysystem" -LSA_HANDLE GetPolicyHandle() -{ - LSA_OBJECT_ATTRIBUTES ObjectAttributes; - WCHAR SystemName[] = TARGET_SYSTEM_NAME; - USHORT SystemNameLength; - LSA_UNICODE_STRING lusSystemName; - NTSTATUS ntsResult; - LSA_HANDLE lsahPolicyHandle; - - // Object attributes are reserved, so initialize to zeroes. - ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); - - //Initialize an LSA_UNICODE_STRING to the server name. - SystemNameLength = wcslen(SystemName); - lusSystemName.Buffer = SystemName; - lusSystemName.Length = SystemNameLength * sizeof(WCHAR); - lusSystemName.MaximumLength = (SystemNameLength+1) * sizeof(WCHAR); - - // Get a handle to the Policy object. - ntsResult = LsaOpenPolicy( - NULL, //Name of the target system. - &ObjectAttributes, //Object attributes. - POLICY_ALL_ACCESS, //Desired access permissions. - &lsahPolicyHandle //Receives the policy handle. - ); - - if (ntsResult != 0) - { - // An error occurred. Display it as a win32 error code. - wprintf(L"OpenPolicy returned %lu\n", - LsaNtStatusToWinError(ntsResult)); - return NULL; - } - return lsahPolicyHandle; -} - - -/***************************************************************** - LoggedSetLockPagesPrivilege: a function to obtain, if possible, or - release the privilege of locking physical pages. - - Inputs: - - HANDLE hProcess: Handle for the process for which the - privilege is needed - - BOOL bEnable: Enable (TRUE) or disable? - - Return value: TRUE indicates success, FALSE failure. - -*****************************************************************/ -BOOL SysLoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable) -{ - struct { - u32 Count; - LUID_AND_ATTRIBUTES Privilege [1]; - } Info; - - HANDLE Token; - BOOL Result; - - // Open the token. - - Result = OpenProcessToken ( hProcess, - TOKEN_ADJUST_PRIVILEGES, - & Token); - - if( Result != TRUE ) { - Console::Error( "VirtualMemory Error > Cannot open process token." ); - return FALSE; - } - - // Enable or disable? - - Info.Count = 1; - if( bEnable ) - { - Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; - } - else - { - Info.Privilege[0].Attributes = SE_PRIVILEGE_REMOVED; - } - - // Get the LUID. - Result = LookupPrivilegeValue ( NULL, - SE_LOCK_MEMORY_NAME, - &(Info.Privilege[0].Luid)); - - if( Result != TRUE ) - { - Console::Error( "VirtualMemory Error > Cannot get privilege value for %s.", params SE_LOCK_MEMORY_NAME ); - return FALSE; - } - - // Adjust the privilege. - - Result = AdjustTokenPrivileges ( Token, FALSE, - (PTOKEN_PRIVILEGES) &Info, - 0, NULL, NULL); - - // Check the result. - if( Result != TRUE ) - { - Console::Error( "VirtualMemory Error > Cannot adjust token privileges, error %u.", params GetLastError() ); - return FALSE; - } - else - { - if( GetLastError() != ERROR_SUCCESS ) - { - - BOOL bSuc = FALSE; - LSA_HANDLE policy; - PLSA_TRANSLATED_SID2 ltsTranslatedSID; - -// if( !DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_USERNAME), gApp.hWnd, (DLGPROC)UserNameProc) ) -// return FALSE; - DWORD len = sizeof(s_szUserName); - GetUserNameW(s_szUserName, &len); - - policy = GetPolicyHandle(); - - if( policy != NULL ) { - - ltsTranslatedSID = GetSIDInformation(s_szUserName, policy); - - if( ltsTranslatedSID != NULL ) { - bSuc = AddPrivileges(ltsTranslatedSID->Sid, policy, bEnable); - LsaFreeMemory(ltsTranslatedSID); - } - - LsaClose(policy); - } - - if( bSuc ) { - // Get the LUID. - LookupPrivilegeValue ( NULL, SE_LOCK_MEMORY_NAME, &(Info.Privilege[0].Luid)); - - bSuc = AdjustTokenPrivileges ( Token, FALSE, (PTOKEN_PRIVILEGES) &Info, 0, NULL, NULL); - } - - if( bSuc ) { - if( MessageBox(NULL, "PCSX2 just changed your SE_LOCK_MEMORY privilege in order to gain access to physical memory.\n" - "Log off/on and run pcsx2 again. Do you want to log off?\n", - "Privilege changed query", MB_YESNO) == IDYES ) { - ExitWindows(EWX_LOGOFF, 0); - } - } - else { - MessageBox(NULL, "Failed adding SE_LOCK_MEMORY privilege, please check the local policy.\n" - "Go to security settings->Local Policies->User Rights. There should be a \"Lock pages in memory\".\n" - "Add your user to that and log off/on. This enables pcsx2 to run at real-time by allocating physical memory.\n" - "Also can try Control Panel->Local Security Policy->... (this does not work on Windows XP Home)\n" - "(zerofrog)\n", "Virtual Memory Access Denied", MB_OK); - return FALSE; - } - } - } - - CloseHandle( Token ); - - return TRUE; -} - -static u32 s_dwPageSize = 0; -int SysPhysicalAlloc(u32 size, PSMEMORYBLOCK* pblock) -{ -//#ifdef WIN32_FILE_MAPPING -// assert(0); -//#endif - ULONG_PTR NumberOfPagesInitial; // initial number of pages requested - int PFNArraySize; // memory to request for PFN array - BOOL bResult; - - assert( pblock != NULL ); - memset(pblock, 0, sizeof(PSMEMORYBLOCK)); - - if( s_dwPageSize == 0 ) { - SYSTEM_INFO sSysInfo; // useful system information - GetSystemInfo(&sSysInfo); // fill the system information structure - s_dwPageSize = sSysInfo.dwPageSize; - - if( s_dwPageSize != 0x1000 ) { - Msgbox::Alert("Error! OS page size must be 4Kb!\n" - "If for some reason the OS cannot have 4Kb pages, then run the TLB build."); - return -1; - } - } - - // Calculate the number of pages of memory to request. - pblock->NumberPages = (size+s_dwPageSize-1)/s_dwPageSize; - PFNArraySize = pblock->NumberPages * sizeof (ULONG_PTR); - - pblock->aPFNs = (uptr*)HeapAlloc (GetProcessHeap (), 0, PFNArraySize); - - if (pblock->aPFNs == NULL) { - Console::Error("Failed to allocate on heap."); - goto eCleanupAndExit; - } - - // Allocate the physical memory. - NumberOfPagesInitial = pblock->NumberPages; - bResult = AllocateUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); - - if( bResult != TRUE ) - { - Console::Error("Virtual Memory Error %u > Cannot allocate physical pages.", params GetLastError() ); - goto eCleanupAndExit; - } - - if( NumberOfPagesInitial != pblock->NumberPages ) - { - Console::Error("Virtual Memory > Physical allocation failed!\n\tAllocated only %p of %p pages.", params pblock->NumberPages, NumberOfPagesInitial ); - goto eCleanupAndExit; - } - - pblock->aVFNs = (uptr*)HeapAlloc(GetProcessHeap(), 0, PFNArraySize); - - return 0; - -eCleanupAndExit: - SysPhysicalFree(pblock); - return -1; -} - -void SysPhysicalFree(PSMEMORYBLOCK* pblock) -{ - assert( pblock != NULL ); - - // Free the physical pages. - FreeUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); - - if( pblock->aPFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aPFNs); - if( pblock->aVFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aVFNs); - memset(pblock, 0, sizeof(PSMEMORYBLOCK)); -} - -int SysVirtualPhyAlloc(void* base, u32 size, PSMEMORYBLOCK* pblock) -{ - BOOL bResult; - int i; - - LPVOID lpMemReserved = VirtualAlloc( base, size, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE ); - if( lpMemReserved == NULL || base != lpMemReserved ) - { - Console::WriteLn("VirtualMemory Error %d > Cannot reserve memory at 0x%8.8x(%x).", params base, lpMemReserved, GetLastError()); - goto eCleanupAndExit; - } - - // Map the physical memory into the window. - bResult = MapUserPhysicalPages( base, (ULONG_PTR)pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); - - for(i = 0; i < pblock->NumberPages; ++i) - pblock->aVFNs[i] = (uptr)base + 0x1000*i; - - if( bResult != TRUE ) - { - Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to map.", params GetLastError() ); - goto eCleanupAndExit; - } - - return 0; - -eCleanupAndExit: - SysVirtualFree(base, size); - return -1; -} - -void SysVirtualFree(void* lpMemReserved, u32 size) -{ - // unmap - if( MapUserPhysicalPages( lpMemReserved, (size+s_dwPageSize-1)/s_dwPageSize, NULL ) != TRUE ) - { - Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to unmap", params GetLastError() ); - return; - } - - // Free virtual memory. - VirtualFree( lpMemReserved, 0, MEM_RELEASE ); -} - -int SysMapUserPhysicalPages(void* Addr, uptr NumPages, uptr* pfn, int pageoffset) -{ - BOOL bResult = MapUserPhysicalPages(Addr, NumPages, (PULONG_PTR)(pfn+pageoffset)); - -#ifdef _DEBUG - //if( !bResult ) - //__Log("Failed to map user pages: 0x%x:0x%x, error = %d\n", Addr, NumPages, GetLastError()); -#endif - - return bResult; -} - -#else - -#endif diff --git a/pcsx2/x86/iGS.cpp b/pcsx2/x86/iGS.cpp deleted file mode 100644 index aa0bf63a9c..0000000000 --- a/pcsx2/x86/iGS.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator - * Copyright (C) 2002-2009 Pcsx2 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 "PrecompiledHeader.h" - -#include "Common.h" -#include "VU.h" - -#include "iR5900.h" - -#include "GS.h" -#include "DebugTools/Debug.h" - -extern u8 g_RealGSMem[0x2000]; -#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff)) - -// __thiscall -- Calling Convention Notes. - -// ** MSVC passes the pointer to the object as ECX. Other parameters are passed normally -// (_cdecl style). Stack is cleaned by the callee. - -// ** GCC works just like a __cdecl, except the pointer to the object is pushed onto the -// stack last (passed as the first parameter). Caller cleans up the stack. - -// The GCC code below is untested. Hope it works. :| (air) - - -// Used to send 8, 16, and 32 bit values to the MTGS. -static void __fastcall _rec_mtgs_Send32orSmaller( GS_RINGTYPE ringtype, u32 mem, int mmreg ) -{ - iFlushCall(0); - - PUSH32I( 0 ); - _callPushArg( mmreg, 0 ); - PUSH32I( mem&0x13ff ); - PUSH32I( ringtype ); - -#ifdef _MSC_VER - MOV32ItoR( ECX, (uptr)mtgsThread ); - CALLFunc( mtgsThread->FnPtr_SimplePacket() ); -#else // GCC --> - PUSH32I( (uptr)mtgsThread ); - CALLFunc( mtgsThread->FnPtr_SimplePacket() ); - ADD32ItoR( ESP, 20 ); -#endif -} - -// Used to send 64 and 128 bit values to the MTGS (called twice for 128's, which -// is why it doesn't call iFlushCall) -static void __fastcall _rec_mtgs_Send64( uptr gsbase, u32 mem, int mmreg ) -{ - PUSH32M( gsbase+4 ); - PUSH32M( gsbase ); - PUSH32I( mem&0x13ff ); - PUSH32I( GS_RINGTYPE_MEMWRITE64 ); - -#ifdef _MSC_VER - MOV32ItoR( ECX, (uptr)mtgsThread ); - CALLFunc( mtgsThread->FnPtr_SimplePacket() ); -#else // GCC --> - PUSH32I( (uptr)mtgsThread ); - CALLFunc( mtgsThread->FnPtr_SimplePacket() ); - ADD32ItoR( ESP, 20 ); -#endif - -} - -void gsConstWrite8(u32 mem, int mmreg) -{ - switch (mem&~3) { - case 0x12001000: // GS_CSR - _eeMoveMMREGtoR(EAX, mmreg); - iFlushCall(0); - MOV32MtoR(ECX, (uptr)&CSRw); - AND32ItoR(EAX, 0xff<<(mem&3)*8); - AND32ItoR(ECX, ~(0xff<<(mem&3)*8)); - OR32ItoR(EAX, ECX); - _callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0); - break; - default: - _eeWriteConstMem8( (uptr)PS2GS_BASE(mem), mmreg ); - - if( mtgsThread != NULL ) - _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE8, mem, mmreg ); - - break; - } -} - -void gsConstWrite16(u32 mem, int mmreg) -{ - switch (mem&~3) { - - case 0x12000010: // GS_SMODE1 - case 0x12000020: // GS_SMODE2 - // SMODE1 and SMODE2 fall back on the gsWrite library. - iFlushCall(0); - _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); - break; - - case 0x12001000: // GS_CSR - - assert( !(mem&2) ); - _eeMoveMMREGtoR(EAX, mmreg); - iFlushCall(0); - - MOV32MtoR(ECX, (uptr)&CSRw); - AND32ItoR(EAX, 0xffff<<(mem&2)*8); - AND32ItoR(ECX, ~(0xffff<<(mem&2)*8)); - OR32ItoR(EAX, ECX); - _callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0); - break; - - default: - _eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg ); - - if( mtgsThread != NULL ) - _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE16, mem, mmreg ); - - break; - } -} - -// (value&0x1f00)|0x6000 -void gsConstWriteIMR(int mmreg) -{ - const u32 mem = 0x12001010; - if( mmreg & MEM_XMMTAG ) { - SSE2_MOVD_XMM_to_M32((uptr)PS2GS_BASE(mem), mmreg&0xf); - AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00); - OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000); - } - else if( mmreg & MEM_MMXTAG ) { - SetMMXstate(); - MOVDMMXtoM((uptr)PS2GS_BASE(mem), mmreg&0xf); - AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00); - OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000); - } - else if( mmreg & MEM_EECONSTTAG ) { - MOV32ItoM( (uptr)PS2GS_BASE(mem), (g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x1f00)|0x6000); - } - else { - AND32ItoR(mmreg, 0x1f00); - OR32ItoR(mmreg, 0x6000); - MOV32RtoM( (uptr)PS2GS_BASE(mem), mmreg ); - } - - // IMR doesn't need to be updated in MTGS mode -} - -void gsConstWrite32(u32 mem, int mmreg) { - - switch (mem) { - - case 0x12000010: // GS_SMODE1 - case 0x12000020: // GS_SMODE2 - // SMODE1 and SMODE2 fall back on the gsWrite library. - iFlushCall(0); - _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); - break; - - case 0x12001000: // GS_CSR - iFlushCall(0); - _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); - break; - - case 0x12001010: // GS_IMR - gsConstWriteIMR(mmreg); - break; - default: - _eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg ); - - if( mtgsThread != NULL ) - _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE32, mem, mmreg ); - - break; - } -} - -void gsConstWrite64(u32 mem, int mmreg) -{ - switch (mem) { - case 0x12000010: // GS_SMODE1 - case 0x12000020: // GS_SMODE2 - // SMODE1 and SMODE2 fall back on the gsWrite library. - // the low 32 bit dword is all the SMODE regs care about. - iFlushCall(0); - _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); - break; - - case 0x12001000: // GS_CSR - iFlushCall(0); - _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); - break; - - case 0x12001010: // GS_IMR - gsConstWriteIMR(mmreg); - break; - - default: - _eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg); - - if( mtgsThread != NULL ) - { - iFlushCall( 0 ); - _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg ); - } - - break; - } -} - -void gsConstWrite128(u32 mem, int mmreg) -{ - switch (mem) { - case 0x12000010: // GS_SMODE1 - case 0x12000020: // GS_SMODE2 - // SMODE1 and SMODE2 fall back on the gsWrite library. - // the low 32 bit dword is all the SMODE regs care about. - iFlushCall(0); - _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); - break; - - case 0x12001000: // GS_CSR - iFlushCall(0); - _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); - break; - - case 0x12001010: // GS_IMR - // (value&0x1f00)|0x6000 - gsConstWriteIMR(mmreg); - break; - - default: - _eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg); - - if( mtgsThread != NULL ) - { - iFlushCall(0); - _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg ); - _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem)+8, mem+8, mmreg ); - } - - break; - } -} - -int gsConstRead8(u32 x86reg, u32 mem, u32 sign) -{ - GIF_LOG("GS read 8 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); - _eeReadConstMem8(x86reg, (uptr)PS2GS_BASE(mem), sign); - return 0; -} - -int gsConstRead16(u32 x86reg, u32 mem, u32 sign) -{ - GIF_LOG("GS read 16 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); - _eeReadConstMem16(x86reg, (uptr)PS2GS_BASE(mem), sign); - return 0; -} - -int gsConstRead32(u32 x86reg, u32 mem) -{ - GIF_LOG("GS read 32 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); - _eeReadConstMem32(x86reg, (uptr)PS2GS_BASE(mem)); - return 0; -} - -void gsConstRead64(u32 mem, int mmreg) -{ - GIF_LOG("GS read 64 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); - if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (uptr)PS2GS_BASE(mem)); - else { - MOVQMtoR(mmreg, (uptr)PS2GS_BASE(mem)); - SetMMXstate(); - } -} - -void gsConstRead128(u32 mem, int xmmreg) -{ - GIF_LOG("GS read 128 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); - _eeReadConstMem128( xmmreg, (uptr)PS2GS_BASE(mem)); -} diff --git a/pcsx2/x86/iHw.cpp b/pcsx2/x86/iHw.cpp deleted file mode 100644 index b59744dca8..0000000000 --- a/pcsx2/x86/iHw.cpp +++ /dev/null @@ -1,1145 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator - * Copyright (C) 2002-2009 Pcsx2 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 "PrecompiledHeader.h" - -#include "Common.h" -#include "iR5900.h" -#include "VUmicro.h" -#include "IopMem.h" - -// The full suite of hardware APIs: -#include "IPU/IPU.h" -#include "GS.h" -#include "Counters.h" -#include "Vif.h" -#include "VifDma.h" -#include "SPR.h" -#include "Sif.h" - - -extern int rdram_devices; // put 8 for TOOL and 2 for PS2 and PSX -extern int rdram_sdevid; -extern char sio_buffer[1024]; -extern int sio_count; - -int hwConstRead8(u32 x86reg, u32 mem, u32 sign) -{ - if( mem >= 0x10000000 && mem < 0x10008000 ) - DevCon::WriteLn("hwRead8 to %x", params mem); - - if ((mem & 0xffffff0f) == 0x1000f200) { - if(mem == 0x1000f260) { - MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); - else) - XOR32RtoR(x86reg, x86reg); - return 0; - } - else if(mem == 0x1000F240) { - - _eeReadConstMem8(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); - //psHu32(mem) &= ~0x4000; - return 0; - } - } - - if (mem < 0x10010000) - { - _eeReadConstMem8(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); - } - else { - MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); - else ) - XOR32RtoR(x86reg, x86reg); - } - - return 0; -} - -#define CONSTREAD16_CALL(name) { \ - iFlushCall(0); \ - CALLFunc((uptr)name); \ - if( sign ) MOVSX32R16toR(EAX, EAX); \ - else MOVZX32R16toR(EAX, EAX); \ -} \ - -static u32 s_regreads[3] = {0x010200000, 0xbfff0000, 0xF0000102}; -int hwConstRead16(u32 x86reg, u32 mem, u32 sign) -{ - if( mem >= 0x10002000 && mem < 0x10008000 ) - DevCon::WriteLn("hwRead16 to %x", params mem); - - if( mem >= 0x10000000 && mem < 0x10002000 ) - EECNT_LOG("cnt read to %x\n", params mem); - - switch (mem) { - case 0x10000000: - PUSH32I(0); - CONSTREAD16_CALL(rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - case 0x10000010: - _eeReadConstMem16(x86reg, (uptr)&counters[0].mode, sign); - return 0; - case 0x10000020: - _eeReadConstMem16(x86reg, (uptr)&counters[0].mode, sign); - return 0; - case 0x10000030: - _eeReadConstMem16(x86reg, (uptr)&counters[0].hold, sign); - return 0; - - case 0x10000800: - PUSH32I(1); - CONSTREAD16_CALL(rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - - case 0x10000810: - _eeReadConstMem16(x86reg, (uptr)&counters[1].mode, sign); - return 0; - - case 0x10000820: - _eeReadConstMem16(x86reg, (uptr)&counters[1].target, sign); - return 0; - - case 0x10000830: - _eeReadConstMem16(x86reg, (uptr)&counters[1].hold, sign); - return 0; - - case 0x10001000: - PUSH32I(2); - CONSTREAD16_CALL(rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - - case 0x10001010: - _eeReadConstMem16(x86reg, (uptr)&counters[2].mode, sign); - return 0; - - case 0x10001020: - _eeReadConstMem16(x86reg, (uptr)&counters[2].target, sign); - return 0; - - case 0x10001800: - PUSH32I(3); - CONSTREAD16_CALL(rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - - case 0x10001810: - _eeReadConstMem16(x86reg, (uptr)&counters[3].mode, sign); - return 0; - - case 0x10001820: - _eeReadConstMem16(x86reg, (uptr)&counters[3].target, sign); - return 0; - - default: - if ((mem & 0xffffff0f) == 0x1000f200) { - if(mem == 0x1000f260) { - MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); - else ) - XOR32RtoR(x86reg, x86reg); - return 0; - } - else if(mem == 0x1000F240) { - - MMXONLY(if( IS_MMXREG(x86reg) ) { - MOVDMtoMMX(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff] - 2); - PORMtoR(x86reg&0xf, (uptr)&s_regreads[0]); - PANDMtoR(x86reg&0xf, (uptr)&s_regreads[1]); - } - else ) - { - if( sign ) MOVSX32M16toR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - else MOVZX32M16toR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - - OR32ItoR(x86reg, 0x0102); - AND32ItoR(x86reg, ~0x4000); - } - return 0; - } - } - if (mem < 0x10010000) { - _eeReadConstMem16(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); - } - else { - MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); - else ) - XOR32RtoR(x86reg, x86reg); - } - - return 0; - } -} - -int hwContRead32_f440() -{ - if ((psHu32(0xf430) >> 6) & 0xF) - return 0; - else - switch ((psHu32(0xf430)>>16) & 0xFFF){//MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 - case 0x21://INIT - { - int ret = 0x1F * (rdram_sdevid < rdram_devices); - rdram_sdevid += (rdram_sdevid < rdram_devices); - return ret; - } - case 0x23://CNFGA - return 0x0D0D; //PVER=3 | MVER=16 | DBL=1 | REFBIT=5 - case 0x24://CNFGB - //0x0110 for PSX SVER=0 | CORG=8(5x9x7) | SPT=1 | DEVTYP=0 | BYTE=0 - return 0x0090; //SVER=0 | CORG=4(5x9x6) | SPT=1 | DEVTYP=0 | BYTE=0 - case 0x40://DEVID - return psHu32(0xf430) & 0x1F; // =SDEV - } - - return 0; -} - -int hwConstRead32(u32 x86reg, u32 mem) -{ - //IPU regs - if ((mem>=0x10002000) && (mem<0x10003000)) { - //return ipuConstRead32(x86reg, mem); - iFlushCall(0); - PUSH32I( mem ); - CALLFunc( (uptr)ipuRead32 ); - } - - switch (mem) { - case 0x10000000: - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - case 0x10000010: - _eeReadConstMem32(x86reg, (uptr)&counters[0].mode); - return 0; - case 0x10000020: - _eeReadConstMem32(x86reg, (uptr)&counters[0].target); - return 0; - case 0x10000030: - _eeReadConstMem32(x86reg, (uptr)&counters[0].hold); - return 0; - - case 0x10000800: - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - case 0x10000810: - _eeReadConstMem32(x86reg, (uptr)&counters[1].mode); - return 0; - case 0x10000820: - _eeReadConstMem32(x86reg, (uptr)&counters[1].target); - return 0; - case 0x10000830: - _eeReadConstMem32(x86reg, (uptr)&counters[1].hold); - return 0; - - case 0x10001000: - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - case 0x10001010: - _eeReadConstMem32(x86reg, (uptr)&counters[2].mode); - return 0; - case 0x10001020: - _eeReadConstMem32(x86reg, (uptr)&counters[2].target); - return 0; - case 0x10001030: - // fixme: Counters[2].hold and Counters[3].hold are never assigned values - // anywhere in Pcsx2. - _eeReadConstMem32(x86reg, (uptr)&counters[2].hold); - return 0; - - case 0x10001800: - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)rcntRcount); - ADD32ItoR(ESP, 4); - return 1; - case 0x10001810: - _eeReadConstMem32(x86reg, (uptr)&counters[3].mode); - return 0; - case 0x10001820: - _eeReadConstMem32(x86reg, (uptr)&counters[3].target); - return 0; - case 0x10001830: - // fixme: Counters[2].hold and Counters[3].hold are never assigned values - // anywhere in Pcsx2. - _eeReadConstMem32(x86reg, (uptr)&counters[3].hold); - return 0; - - case 0x1000f130: - case 0x1000f410: - case 0x1000f430: - if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); - MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) - else XOR32RtoR(x86reg, x86reg); - return 0; - - case 0x1000f440: - iFlushCall(0); - CALLFunc((uptr)hwContRead32_f440); - return 1; - - case 0x1000f520: // DMAC_ENABLER - _eeReadConstMem32(x86reg, (uptr)&PS2MEM_HW[0xf590]); - return 0; - - default: - if ((mem & 0xffffff0f) == 0x1000f200) { - if(mem == 0x1000f260) { - if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); - MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) - else XOR32RtoR(x86reg, x86reg); - return 0; - } - else if(mem == 0x1000F240) { - - if( IS_XMMREG(x86reg) ) { - SSEX_MOVD_M32_to_XMM(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - SSEX_POR_M128_to_XMM(x86reg&0xf, (uptr)&s_regreads[2]); - } - MMXONLY(else if( IS_MMXREG(x86reg) ) { - MOVDMtoMMX(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - PORMtoR(x86reg&0xf, (uptr)&s_regreads[2]); - }) - else { - MOV32MtoR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - OR32ItoR(x86reg, 0xF0000102); - } - return 0; - } - } - - if (mem < 0x10010000) { - _eeReadConstMem32(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); - } - else { - if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); - MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) - else XOR32RtoR(x86reg, x86reg); - } - - return 0; - } -} - -void hwConstRead64(u32 mem, int mmreg) { - if ((mem>=0x10002000) && (mem<0x10003000)) { - ipuConstRead64(mem, mmreg); - return; - } - - if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (uptr)PSM(mem)); - else { - MMXONLY(MOVQMtoR(mmreg, (uptr)PSM(mem)); - SetMMXstate();) - } -} - -PCSX2_ALIGNED16(u32 s_TempFIFO[4]); -void hwConstRead128(u32 mem, int xmmreg) { - - // fixme : This needs to be updated to use the new paged FIFO accessors. - /*if (mem >= 0x10004000 && mem < 0x10008000) { - iFlushCall(0); - PUSH32I((uptr)&s_TempFIFO[0]); - PUSH32I(mem); - CALLFunc((uptr)ReadFIFO); - ADD32ItoR(ESP, 8); - _eeReadConstMem128( xmmreg, (uptr)&s_TempFIFO[0]); - return; - }*/ - - _eeReadConstMem128( xmmreg, (uptr)PSM(mem)); -} - -// when writing imm -static void recDmaExecI8(void (*name)(), u32 mem, int mmreg) -{ - MOV8ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); - if( g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 1 ) { - TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - CALLFunc((uptr)name); - x86SetJ8( j8Ptr[6] ); - } -} - -static void recDmaExec8(void (*name)(), u32 mem, int mmreg) -{ - // Flushcall Note : DMA transfers are almost always "involved" operations - // that use memcpys and/or threading. Freeing all XMM and MMX regs is the - // best option. - - iFlushCall(FLUSH_NOCONST); - if( IS_EECONSTREG(mmreg) ) { - recDmaExecI8(name, mem, mmreg); - } - else { - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem8((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); - - TEST8ItoR(EAX, 1); - j8Ptr[5] = JZ8(0); - TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - - CALLFunc((uptr)name); - - x86SetJ8( j8Ptr[5] ); - x86SetJ8( j8Ptr[6] ); - } -} - -static void __fastcall PrintDebug(u8 value) -{ - // Note: This is where the EE's diagonstic messages originate from (like the ones that - // start with hash # marks) - - if (value == '\n') { - sio_buffer[sio_count] = 0; - Console::WriteLn( Color_Cyan, sio_buffer ); - sio_count = 0; - } else { - if (sio_count < 1023) { - sio_buffer[sio_count++] = value; - } - } -} - -// fixme: this would be more optimal as a C++ template (with bit as the template parameter) -template< uint bit > -static void ConstWrite_ExecTimer( uptr func, u8 index, int mmreg) -{ - if( bit != 32 ) - { - if( !IS_EECONSTREG(mmreg) ) - { - if( bit == 8 ) MOVZX32R8toR(mmreg&0xf, mmreg&0xf); - else if( bit == 16 ) MOVZX32R16toR(mmreg&0xf, mmreg&0xf); - } - } - - // FlushCall Note : All counter functions are short and sweet, full flush not needed. - - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(index); - CALLFunc(func); - ADD32ItoR(ESP, 8); -} - -#define CONSTWRITE_TIMERS(bit) \ - case 0x10000000: ConstWrite_ExecTimer<bit>((uptr)&rcntWcount, 0, mmreg); break; \ - case 0x10000010: ConstWrite_ExecTimer<bit>((uptr)&rcntWmode, 0, mmreg); break; \ - case 0x10000020: ConstWrite_ExecTimer<bit>((uptr)&rcntWtarget, 0, mmreg); break; \ - case 0x10000030: ConstWrite_ExecTimer<bit>((uptr)&rcntWhold, 0, mmreg); break; \ - \ - case 0x10000800: ConstWrite_ExecTimer<bit>((uptr)&rcntWcount, 1, mmreg); break; \ - case 0x10000810: ConstWrite_ExecTimer<bit>((uptr)&rcntWmode, 1, mmreg); break; \ - case 0x10000820: ConstWrite_ExecTimer<bit>((uptr)&rcntWtarget, 1, mmreg); break; \ - case 0x10000830: ConstWrite_ExecTimer<bit>((uptr)&rcntWhold, 1, mmreg); break; \ - \ - case 0x10001000: ConstWrite_ExecTimer<bit>((uptr)&rcntWcount, 2, mmreg); break; \ - case 0x10001010: ConstWrite_ExecTimer<bit>((uptr)&rcntWmode, 2, mmreg); break; \ - case 0x10001020: ConstWrite_ExecTimer<bit>((uptr)&rcntWtarget, 2, mmreg); break; \ - \ - case 0x10001800: ConstWrite_ExecTimer<bit>((uptr)&rcntWcount, 3, mmreg); break; \ - case 0x10001810: ConstWrite_ExecTimer<bit>((uptr)&rcntWmode, 3, mmreg); break; \ - case 0x10001820: ConstWrite_ExecTimer<bit>((uptr)&rcntWtarget, 3, mmreg); break; \ - -void hwConstWrite8(u32 mem, int mmreg) -{ - switch (mem) { - CONSTWRITE_TIMERS(8) - - case 0x1000f180: - // Yay fastcall! - _eeMoveMMREGtoR( ECX, mmreg ); - iFlushCall(0); - CALLFunc((uptr)PrintDebug); - break; - - case 0x10008001: // dma0 - vif0 - recDmaExec8(dmaVIF0, mem, mmreg); - break; - - case 0x10009001: // dma1 - vif1 - recDmaExec8(dmaVIF1, mem, mmreg); - break; - - case 0x1000a001: // dma2 - gif - recDmaExec8(dmaGIF, mem, mmreg); - break; - - case 0x1000b001: // dma3 - fromIPU - recDmaExec8(dmaIPU0, mem, mmreg); - break; - - case 0x1000b401: // dma4 - toIPU - recDmaExec8(dmaIPU1, mem, mmreg); - break; - - case 0x1000c001: // dma5 - sif0 - //if (value == 0) psxSu32(0x30) = 0x40000; - recDmaExec8(dmaSIF0, mem, mmreg); - break; - - case 0x1000c401: // dma6 - sif1 - recDmaExec8(dmaSIF1, mem, mmreg); - break; - - case 0x1000c801: // dma7 - sif2 - recDmaExec8(dmaSIF2, mem, mmreg); - break; - - case 0x1000d001: // dma8 - fromSPR - recDmaExec8(dmaSPR0, mem, mmreg); - break; - - case 0x1000d401: // dma9 - toSPR - recDmaExec8(dmaSPR1, mem, mmreg); - break; - - case 0x1000f592: // DMAC_ENABLEW - _eeWriteConstMem8( (uptr)&PS2MEM_HW[0xf522], mmreg ); - _eeWriteConstMem8( (uptr)&PS2MEM_HW[0xf592], mmreg ); - break; - - default: - if ((mem & 0xffffff0f) == 0x1000f200) { - u32 at = mem & 0xf0; - switch(at) - { - case 0x00: - _eeWriteConstMem8( (uptr)&PS2MEM_HW[mem&0xffff], mmreg); - break; - case 0x40: - if( IS_EECONSTREG(mmreg) ) { - if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { - AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); - } - } - else { - _eeMoveMMREGtoR(EAX, mmreg); - TEST16ItoR(EAX, 0x100); - j8Ptr[5] = JNZ8(0); - AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); - x86SetJ8(j8Ptr[5]); - } - break; - } - return; - } - assert( (mem&0xff0f) != 0xf200 ); - - switch(mem&~3) { - case 0x1000f130: - case 0x1000f410: - case 0x1000f430: - break; - default: - _eeWriteConstMem8((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - } - - break; - } -} - -// Flushcall Note : DMA transfers are almost always "involved" operations -// that use memcpys and/or threading. Freeing all XMM and MMX regs is the -// best option (removes the need for FreezeXMMRegs()). But register -// allocation is such a mess right now that we can't do it (yet). - -static void recDmaExecI16( void (*name)(), u32 mem, int mmreg ) -{ - MOV16ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); - if( g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100 ) { - TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - CALLFunc((uptr)name); - x86SetJ8( j8Ptr[6] ); - } -} - -static void recDmaExec16(void (*name)(), u32 mem, int mmreg) -{ - iFlushCall(0); - - if( IS_EECONSTREG(mmreg) ) { - recDmaExecI16(name, mem, mmreg); - } - else { - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem16((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); - - TEST16ItoR(EAX, 0x100); - j8Ptr[5] = JZ8(0); - TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - - CALLFunc((uptr)name); - - x86SetJ8( j8Ptr[5] ); - x86SetJ8( j8Ptr[6] ); - } -} - -void hwConstWrite16(u32 mem, int mmreg) -{ - switch(mem) { - - CONSTWRITE_TIMERS(16) - - case 0x10008000: // dma0 - vif0 - recDmaExec16(dmaVIF0, mem, mmreg); - break; - - case 0x10009000: // dma1 - vif1 - chcr - recDmaExec16(dmaVIF1, mem, mmreg); - break; - - case 0x1000a000: // dma2 - gif - recDmaExec16(dmaGIF, mem, mmreg); - break; - case 0x1000b000: // dma3 - fromIPU - recDmaExec16(dmaIPU0, mem, mmreg); - break; - case 0x1000b400: // dma4 - toIPU - recDmaExec16(dmaIPU1, mem, mmreg); - break; - case 0x1000c000: // dma5 - sif0 - //if (value == 0) psxSu32(0x30) = 0x40000; - recDmaExec16(dmaSIF0, mem, mmreg); - break; - case 0x1000c002: - //? - break; - case 0x1000c400: // dma6 - sif1 - recDmaExec16(dmaSIF1, mem, mmreg); - break; - case 0x1000c800: // dma7 - sif2 - recDmaExec16(dmaSIF2, mem, mmreg); - break; - case 0x1000c802: - //? - break; - case 0x1000d000: // dma8 - fromSPR - recDmaExec16(dmaSPR0, mem, mmreg); - break; - case 0x1000d400: // dma9 - toSPR - recDmaExec16(dmaSPR1, mem, mmreg); - break; - case 0x1000f592: // DMAC_ENABLEW - _eeWriteConstMem16((uptr)&PS2MEM_HW[0xf522], mmreg); - _eeWriteConstMem16((uptr)&PS2MEM_HW[0xf592], mmreg); - break; - case 0x1000f130: - case 0x1000f410: - case 0x1000f430: - break; - default: - if ((mem & 0xffffff0f) == 0x1000f200) { - u32 at = mem & 0xf0; - switch(at) - { - case 0x00: - _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - break; - case 0x20: - _eeWriteConstMem16OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 1); - break; - case 0x30: - if( IS_EECONSTREG(mmreg) ) { - AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); - } - else { - NOT32R(mmreg&0xf); - AND16RtoM((uptr)&PS2MEM_HW[mem&0xffff], mmreg&0xf); - } - break; - case 0x40: - if( IS_EECONSTREG(mmreg) ) { - if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { - AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); - } - else { - OR16ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); - } - } - else { - _eeMoveMMREGtoR(EAX, mmreg); - TEST16ItoR(EAX, 0x100); - j8Ptr[5] = JZ8(0); - OR16ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); - - x86SetJ8( j8Ptr[6] ); - } - - break; - case 0x60: - _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], 0); - break; - } - return; - } - - _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - } -} - -// when writing an Imm - -static void recDmaExecI( void (*name)(), u32 mem, int mmreg ) -{ - u32 c = g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]; - /* Keep the old tag if in chain mode and hw doesnt set it*/ - if( (c & 0xc) == 0x4 && (c&0xffff0000) == 0 ) { - MOV16ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], c); - } - else MOV32ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], c); - if( c & 0x100 ) { - TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - CALLFunc((uptr)name); - x86SetJ8( j8Ptr[6] ); - } -} - -static void recDmaExec( void (*name)(), u32 mem, int mmreg ) -{ - - iFlushCall(0); - - if( IS_EECONSTREG(mmreg) ) { - recDmaExecI(name, mem, mmreg); - } - else { - - // fixme: This is a lot of code to be injecting into the recompiler - // for every DMA transfer. It might actually be more efficient to - // set this up as a C function call instead (depends on how often - // the register is written without actually starting a DMA xfer). - - _eeMoveMMREGtoR(EAX, mmreg); - TEST32ItoR(EAX, 0xffff0000); - j8Ptr[6] = JNZ8(0); - MOV32RtoR(ECX, EAX); - AND32ItoR(ECX, 0xc); - CMP32ItoR(ECX, 4); - j8Ptr[7] = JNE8(0); - if( IS_XMMREG(mmreg) || IS_MMXREG(mmreg) ) { - MOV16RtoM((uptr)&PS2MEM_HW[(mem) & 0xffff], EAX); - } - else { - _eeWriteConstMem16((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); - } - j8Ptr[8] = JMP8(0); - x86SetJ8(j8Ptr[6]); - x86SetJ8(j8Ptr[7]); - _eeWriteConstMem32((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); - x86SetJ8(j8Ptr[8]); - - TEST16ItoR(EAX, 0x100); - j8Ptr[5] = JZ8(0); - TEST32ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); - j8Ptr[6] = JZ8(0); - - CALLFunc((uptr)name); - x86SetJ8( j8Ptr[5] ); - x86SetJ8( j8Ptr[6] ); - } -} - - -void hwConstWrite32(u32 mem, int mmreg) -{ - //IPU regs - if ((mem>=0x10002000) && (mem<0x10003000)) { - //psHu32(mem) = value; - ipuConstWrite32(mem, mmreg); - return; - } - - if ((mem>=0x10003800) && (mem<0x10003c00)) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((uptr)vif0Write32); - ADD32ItoR(ESP, 8); - return; - } - if ((mem>=0x10003c00) && (mem<0x10004000)) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((uptr)vif1Write32); - ADD32ItoR(ESP, 8); - return; - } - - switch (mem) { - - CONSTWRITE_TIMERS(32) - - case GIF_CTRL: - - _eeMoveMMREGtoR(EAX, mmreg); - - iFlushCall(0); - TEST8ItoR(EAX, 1); - j8Ptr[5] = JZ8(0); - - // reset GS - CALLFunc((uptr)gsGIFReset); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND32I8toR(EAX, 8); - MOV32RtoM((uptr)&PS2MEM_HW[mem&0xffff], EAX); - - TEST16ItoR(EAX, 8); - j8Ptr[5] = JZ8(0); - OR8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], 8); - j8Ptr[7] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~8); - x86SetJ8( j8Ptr[6] ); - x86SetJ8( j8Ptr[7] ); - return; - - case GIF_MODE: - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~5); - AND8ItoR(EAX, 5); - OR8RtoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], EAX); - return; - - case GIF_STAT: // stat is readonly - return; - - case 0x10008000: // dma0 - vif0 - recDmaExec(dmaVIF0, mem, mmreg); - break; - - case 0x10009000: // dma1 - vif1 - chcr - recDmaExec(dmaVIF1, mem, mmreg); - break; - - case 0x1000a000: // dma2 - gif - recDmaExec(dmaGIF, mem, mmreg); - break; - - case 0x1000b000: // dma3 - fromIPU - recDmaExec(dmaIPU0, mem, mmreg); - break; - case 0x1000b400: // dma4 - toIPU - recDmaExec(dmaIPU1, mem, mmreg); - break; - case 0x1000c000: // dma5 - sif0 - //if (value == 0) psxSu32(0x30) = 0x40000; - recDmaExec(dmaSIF0, mem, mmreg); - break; - - case 0x1000c400: // dma6 - sif1 - recDmaExec(dmaSIF1, mem, mmreg); - break; - - case 0x1000c800: // dma7 - sif2 - recDmaExec(dmaSIF2, mem, mmreg); - break; - - case 0x1000d000: // dma8 - fromSPR - recDmaExec(dmaSPR0, mem, mmreg); - break; - - case 0x1000d400: // dma9 - toSPR - recDmaExec(dmaSPR1, mem, mmreg); - break; - - case 0x1000e010: // DMAC_STAT - _eeMoveMMREGtoR(EAX, mmreg); - iFlushCall(0); - MOV32RtoR(ECX, EAX); - SHR32ItoR(EAX, 16); - NOT32R(ECX); - XOR16RtoM((uptr)&PS2MEM_HW[0xe012], EAX); - AND16RtoM((uptr)&PS2MEM_HW[0xe010], ECX); - - CALLFunc((uptr)cpuTestDMACInts); - break; - - case 0x1000f000: // INTC_STAT - _eeWriteConstMem32OP((uptr)&PS2MEM_HW[0xf000], mmreg, 2); - CALLFunc((uptr)cpuTestINTCInts); - break; - - case 0x1000f010: // INTC_MASK - _eeMoveMMREGtoR(EAX, mmreg); - iFlushCall(0); - XOR16RtoM((uptr)&PS2MEM_HW[0xf010], EAX); - CALLFunc((uptr)cpuTestINTCInts); - break; - - case 0x1000f130: - case 0x1000f410: - break; - - case 0x1000f430://MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 - - //if ((((value >> 16) & 0xFFF) == 0x21) && (((value >> 6) & 0xF) == 1) && (((psHu32(0xf440) >> 7) & 1) == 0))//INIT & SRP=0 - // rdram_sdevid = 0 - _eeMoveMMREGtoR(EAX, mmreg); - MOV32RtoR(EDX, EAX); - MOV32RtoR(ECX, EAX); - SHR32ItoR(EAX, 6); - SHR32ItoR(EDX, 16); - AND32ItoR(EAX, 0xf); - AND32ItoR(EDX, 0xfff); - CMP32ItoR(EAX, 1); - j8Ptr[5] = JNE8(0); - CMP32ItoR(EDX, 0x21); - j8Ptr[6] = JNE8(0); - - TEST32ItoM((uptr)&psHu32(0xf440), 0x80); - j8Ptr[7] = JNZ8(0); - - // if SIO repeater is cleared, reset sdevid - MOV32ItoM((uptr)&rdram_sdevid, 0); - - //kill the busy bit - x86SetJ8(j8Ptr[5]); - x86SetJ8(j8Ptr[6]); - x86SetJ8(j8Ptr[7]); - AND32ItoR(ECX, ~0x80000000); - MOV32RtoM((uptr)&psHu32(0xf430), ECX); - break; - - case 0x1000f440://MCH_DRD: - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf440], mmreg); - break; - - case 0x1000f590: // DMAC_ENABLEW - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); - return; - - default: - if ((mem & 0xffffff0f) == 0x1000f200) { - u32 at = mem & 0xf0; - switch(at) - { - case 0x00: - _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - break; - case 0x20: - _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 1); - break; - case 0x30: - _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 2); - break; - case 0x40: - if( IS_EECONSTREG(mmreg) ) { - if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { - AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); - } - else { - OR32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); - } - } - else { - _eeMoveMMREGtoR(EAX, mmreg); - TEST32ItoR(EAX, 0x100); - j8Ptr[5] = JZ8(0); - OR32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND32ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); - - x86SetJ8( j8Ptr[6] ); - } - - break; - case 0x60: - MOV32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0); - break; - } - return; - } - - _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - break; - } -} - -void hwConstWrite64(u32 mem, int mmreg) -{ - if ((mem>=0x10002000) && (mem<=0x10002030)) { - ipuConstWrite64(mem, mmreg); - return; - } - - if ((mem>=0x10003800) && (mem<0x10003c00)) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((uptr)vif0Write32); - ADD32ItoR(ESP, 8); - return; - } - if ((mem>=0x10003c00) && (mem<0x10004000)) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(mem); - CALLFunc((uptr)vif1Write32); - ADD32ItoR(ESP, 8); - return; - } - - switch (mem) { - case GIF_CTRL: - _eeMoveMMREGtoR(EAX, mmreg); - - iFlushCall(0); - TEST8ItoR(EAX, 1); - j8Ptr[5] = JZ8(0); - - // reset GS - CALLFunc((uptr)gsGIFReset); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND32I8toR(EAX, 8); - MOV32RtoM((uptr)&PS2MEM_HW[mem&0xffff], EAX); - - TEST16ItoR(EAX, 8); - j8Ptr[5] = JZ8(0); - OR8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], 8); - j8Ptr[7] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~8); - x86SetJ8( j8Ptr[6] ); - x86SetJ8( j8Ptr[7] ); - return; - - case GIF_MODE: - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - - AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~5); - AND8ItoR(EAX, 5); - OR8RtoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], EAX); - break; - - case GIF_STAT: // stat is readonly - return; - - case 0x1000a000: // dma2 - gif - recDmaExec(dmaGIF, mem, mmreg); - break; - - case 0x1000e010: // DMAC_STAT - _eeMoveMMREGtoR(EAX, mmreg); - - iFlushCall(0); - MOV32RtoR(ECX, EAX); - SHR32ItoR(EAX, 16); - NOT32R(ECX); - XOR16RtoM((uptr)&PS2MEM_HW[0xe012], EAX); - AND16RtoM((uptr)&PS2MEM_HW[0xe010], ECX); - - CALLFunc((uptr)cpuTestDMACInts); - break; - - case 0x1000f590: // DMAC_ENABLEW - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); - break; - - case 0x1000f000: // INTC_STAT - _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 2); - CALLFunc((uptr)cpuTestINTCInts); - break; - - case 0x1000f010: // INTC_MASK - - _eeMoveMMREGtoR(EAX, mmreg); - - iFlushCall(0); - XOR16RtoM((uptr)&PS2MEM_HW[0xf010], EAX); - CALLFunc((uptr)cpuTestINTCInts); - break; - - case 0x1000f130: - case 0x1000f410: - case 0x1000f430: - break; - default: - - _eeWriteConstMem64((uptr)PSM(mem), mmreg); - break; - } -} - -void hwConstWrite128(u32 mem, int mmreg) -{ - // fixme : This needs to be updated to use the new paged FIFO accessors. - - /*if (mem >= 0x10004000 && mem < 0x10008000) { - _eeWriteConstMem128((uptr)&s_TempFIFO[0], mmreg); - iFlushCall(0); - PUSH32I((uptr)&s_TempFIFO[0]); - PUSH32I(mem); - CALLFunc((uptr)WriteFIFO); - ADD32ItoR(ESP, 8); - return; - }*/ - - switch (mem) { - case 0x1000f590: // DMAC_ENABLEW - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); - _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); - break; - case 0x1000f130: - case 0x1000f410: - case 0x1000f430: - break; - - default: - - _eeWriteConstMem128((uptr)&PS2MEM_HW[mem&0xffff], mmreg); - break; - } -} diff --git a/pcsx2/x86/iIPU.cpp b/pcsx2/x86/iIPU.cpp deleted file mode 100644 index e1f1d429fc..0000000000 --- a/pcsx2/x86/iIPU.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator -* Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" - -#include "Common.h" -#include "iR5900.h" - -#include "IPU.h" - -/////////////////////////////////////////////////////////////////////// -// IPU Register Reads - -int ipuConstRead32(u32 x86reg, u32 mem) -{ - int workingreg, tempreg, tempreg2; - iFlushCall(0); - CALLFunc((u32)IPUProcessInterrupt); - - // if( !(x86reg&(MEM_XMMTAG|MEM_MMXTAG)) ) { - // if( x86reg == EAX ) { - // tempreg = ECX; - // tempreg2 = EDX; - // } - // else if( x86reg == ECX ) { - // tempreg = EAX; - // tempreg2 = EDX; - // } - // else if( x86reg == EDX ) { - // tempreg = EAX; - // tempreg2 = ECX; - // } - // - // workingreg = x86reg; - // } - // else { - workingreg = EAX; - tempreg = ECX; - tempreg2 = EDX; - // } - - switch (mem){ - - case 0x10002010: // IPU_CTRL - - MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32); - AND32ItoR(workingreg, ~0x3f0f); // save OFC - OR8MtoR(workingreg, (u32)&g_BP.IFC); - OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem - - // MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32); - // AND32ItoR(workingreg, ~0x3fff); - // MOV32MtoR(tempreg, (u32)&g_nIPU0Data); - // MOV8MtoR(workingreg, (u32)&g_BP.IFC); - // - // CMP32ItoR(tempreg, 8); - // j8Ptr[5] = JLE8(0); - // MOV32ItoR(tempreg, 8); - // x86SetJ8( j8Ptr[5] ); - // SHL32ItoR(tempreg, 4); - // - // OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem - // OR8RtoR(workingreg, tempreg); - -#ifdef _DEBUG - MOV32RtoM((u32)&ipuRegs->ctrl._u32, workingreg); -#endif - // NOTE: not updating ipuRegs->ctrl - // if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg); - // else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg); - return 1; - - case 0x10002020: // IPU_BP - - assert( (u32)&g_BP.FP + 1 == (u32)&g_BP.bufferhasnew ); - - MOVZX32M8toR(workingreg, (u32)&g_BP.BP); - MOVZX32M8toR(tempreg, (u32)&g_BP.FP); - AND8ItoR(workingreg, 0x7f); - ADD8MtoR(tempreg, (u32)&g_BP.bufferhasnew); - MOV8MtoR(workingreg+4, (u32)&g_BP.IFC); - - SHL32ItoR(tempreg, 16); - OR32RtoR(workingreg, tempreg); - -#ifdef _DEBUG - MOV32RtoM((u32)&ipuRegs->ipubp, workingreg); -#endif - // NOTE: not updating ipuRegs->ipubp - // if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg); - // else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg); - - return 1; - - default: - // ipu repeats every 0x100 - _eeReadConstMem32(x86reg, (u32)(((u8*)ipuRegs)+(mem&0xff))); - return 0; - } - - return 0; -} - -void ipuConstRead64(u32 mem, int mmreg) -{ - iFlushCall(0); - CALLFunc((u32)IPUProcessInterrupt); - - if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (u32)(((u8*)ipuRegs)+(mem&0xff))); - else { - MOVQMtoR(mmreg, (u32)(((u8*)ipuRegs)+(mem&0xff))); - SetMMXstate(); - } -} - -/////////////////////////////////////////////////////////////////////// -// IPU Register Writes! - -void ipuConstWrite32(u32 mem, int mmreg) -{ - iFlushCall(0); - if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) PUSH32R(mmreg); - CALLFunc((u32)IPUProcessInterrupt); - - switch (mem){ - case 0x10002000: // IPU_CMD - if( (mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) _recPushReg(mmreg); - CALLFunc((u32)IPUCMD_WRITE); - ADD32ItoR(ESP, 4); - break; - case 0x10002010: // IPU_CTRL - if( mmreg & MEM_EECONSTTAG ) { - u32 c = g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x47f30000; - - if( c & 0x40000000 ) { - CALLFunc((u32)ipuSoftReset); - } - else { - AND32ItoM((u32)&ipuRegs->ctrl._u32, 0x8000ffff); - OR32ItoM((u32)&ipuRegs->ctrl._u32, c); - } - } - else { - if( mmreg & MEM_XMMTAG ) SSE2_MOVD_XMM_to_R(EAX, mmreg&0xf); - else if( mmreg & MEM_MMXTAG ) MOVD32MMXtoR(EAX, mmreg&0xf); - else POP32R(EAX); - - MOV32MtoR(ECX, (u32)&ipuRegs->ctrl._u32); - AND32ItoR(EAX, 0x47f30000); - AND32ItoR(ECX, 0x8000ffff); - OR32RtoR(EAX, ECX); - MOV32RtoM((u32)&ipuRegs->ctrl._u32, EAX); - - TEST32ItoR(EAX, 0x40000000); - j8Ptr[5] = JZ8(0); - - // reset - CALLFunc((u32)ipuSoftReset); - - x86SetJ8( j8Ptr[5] ); - } - - break; - default: - if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) POP32R(mmreg); - _eeWriteConstMem32((u32)((u8*)ipuRegs + (mem&0xfff)), mmreg); - break; - } -} - -void ipuConstWrite64(u32 mem, int mmreg) -{ - iFlushCall(0); - CALLFunc((u32)IPUProcessInterrupt); - - switch (mem){ - case 0x10002000: - _recPushReg(mmreg); - CALLFunc((u32)IPUCMD_WRITE); - ADD32ItoR(ESP, 4); - break; - - default: - _eeWriteConstMem64( (u32)((u8*)ipuRegs + (mem&0xfff)), mmreg); - break; - } -} diff --git a/pcsx2/x86/iPsxHw.cpp b/pcsx2/x86/iPsxHw.cpp deleted file mode 100644 index 84c617dc0d..0000000000 --- a/pcsx2/x86/iPsxHw.cpp +++ /dev/null @@ -1,1179 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator - * Copyright (C) 2002-2009 Pcsx2 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 "PrecompiledHeader.h" - -#include "PsxCommon.h" -#include "iR5900.h" - -extern int g_pbufi; -extern s8 g_pbuf[1024]; - -#define CONSTREAD8_CALL(name) { \ - iFlushCall(0); \ - CALLFunc((uptr)name); \ - if( sign ) MOVSX32R8toR(EAX, EAX); \ - else MOVZX32R8toR(EAX, EAX); \ -} \ - -static u32 s_16 = 0x10; - -int psxHwConstRead8(u32 x86reg, u32 add, u32 sign) { - - if (add >= 0x1f801600 && add < 0x1f801700) { - PUSH32I(add); - CONSTREAD8_CALL(USBread8); - // since calling from different dll, esp already changed - return 1; - } - - switch (add) { - case 0x1f801040: - CONSTREAD8_CALL(sioRead8); - return 1; - // case 0x1f801050: hard = serial_read8(); break;//for use of serial port ignore for now - -#ifdef PCSX2_DEVBUILD - case 0x1f801100: - case 0x1f801104: - case 0x1f801108: - case 0x1f801110: - case 0x1f801114: - case 0x1f801118: - case 0x1f801120: - case 0x1f801124: - case 0x1f801128: - case 0x1f801480: - case 0x1f801484: - case 0x1f801488: - case 0x1f801490: - case 0x1f801494: - case 0x1f801498: - case 0x1f8014a0: - case 0x1f8014a4: - case 0x1f8014a8: - SysPrintf("8bit counter read %x\n", add); - _eeReadConstMem8(x86reg, (uptr)&psxH[(add) & 0xffff], sign); - return 0; -#endif - - case 0x1f80146e: // DEV9_R_REV - PUSH32I(add); - CONSTREAD8_CALL(DEV9read8); - return 1; - - case 0x1f801800: CONSTREAD8_CALL(cdrRead0); return 1; - case 0x1f801801: CONSTREAD8_CALL(cdrRead1); return 1; - case 0x1f801802: CONSTREAD8_CALL(cdrRead2); return 1; - case 0x1f801803: CONSTREAD8_CALL(cdrRead3); return 1; - - case 0x1f803100: // PS/EE/IOP conf related - if( IS_XMMREG(x86reg) ) SSEX_MOVD_M32_to_XMM(x86reg&0xf, (uptr)&s_16); - MMXONLY(else if( IS_MMXREG(x86reg) ) MOVDMtoMMX(x86reg&0xf, (uptr)&s_16);) - else MOV32ItoR(x86reg, 0x10); - return 0; - - case 0x1F808264: //sio2 serial data feed/fifo_out - CONSTREAD8_CALL(sio2_fifoOut); - return 1; - - default: - _eeReadConstMem8(x86reg, (uptr)&psxH[(add) & 0xffff], sign); - return 0; - } -} - -#define CONSTREAD16_CALL(name) { \ - iFlushCall(0); \ - CALLFunc((uptr)name); \ - if( sign ) MOVSX32R16toR(EAX, EAX); \ - else MOVZX32R16toR(EAX, EAX); \ -} \ - -void psxConstReadCounterMode16(int x86reg, int index, int sign) -{ - if( IS_MMXREG(x86reg) ) { - MMXONLY(MOV16MtoR(ECX, (uptr)&psxCounters[index].mode); - MOVDMtoMMX(x86reg&0xf, (uptr)&psxCounters[index].mode - 2);) - } - else { - if( sign ) MOVSX32M16toR(ECX, (uptr)&psxCounters[index].mode); - else MOVZX32M16toR(ECX, (uptr)&psxCounters[index].mode); - - MOV32RtoR(x86reg, ECX); - } - - AND16ItoR(ECX, ~0x1800); - OR16ItoR(ECX, 0x400); - MOV16RtoM((uptr)&psxCounters[index].mode, ECX); -} - -int psxHwConstRead16(u32 x86reg, u32 add, u32 sign) { - if (add >= 0x1f801600 && add < 0x1f801700) { - PUSH32I(add); - CONSTREAD16_CALL(USBread16); - return 1; - } - - switch (add) { - - case 0x1f801040: - iFlushCall(0); - CALLFunc((uptr)sioRead8); - PUSHR(EAX); - CALLFunc((uptr)sioRead8); - POPR(ECX); - AND32ItoR(ECX, 0xff); - SHL32ItoR(EAX, 8); - OR32RtoR(EAX, ECX); - if( sign ) MOVSX32R16toR(EAX, EAX); - else MOVZX32R16toR(EAX, EAX); - return 1; - - case 0x1f801044: - _eeReadConstMem16(x86reg, (uptr)&sio.StatReg, sign); - return 0; - - case 0x1f801048: - _eeReadConstMem16(x86reg, (uptr)&sio.ModeReg, sign); - return 0; - - case 0x1f80104a: - _eeReadConstMem16(x86reg, (uptr)&sio.CtrlReg, sign); - return 0; - - case 0x1f80104e: - _eeReadConstMem16(x86reg, (uptr)&sio.BaudReg, sign); - return 0; - - // counters[0] - case 0x1f801100: - PUSH32I(0); - CONSTREAD16_CALL(psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801104: - psxConstReadCounterMode16(x86reg, 0, sign); - return 0; - - case 0x1f801108: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[0].target, sign); - return 0; - - // counters[1] - case 0x1f801110: - PUSH32I(1); - CONSTREAD16_CALL(psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801114: - psxConstReadCounterMode16(x86reg, 1, sign); - return 0; - - case 0x1f801118: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[1].target, sign); - return 0; - - // counters[2] - case 0x1f801120: - PUSH32I(2); - CONSTREAD16_CALL(psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801124: - psxConstReadCounterMode16(x86reg, 2, sign); - return 0; - - case 0x1f801128: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[2].target, sign); - return 0; - - case 0x1f80146e: // DEV9_R_REV - PUSH32I(add); - CONSTREAD16_CALL(DEV9read16); - return 1; - - // counters[3] - case 0x1f801480: - PUSH32I(3); - CONSTREAD16_CALL(psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f801484: - psxConstReadCounterMode16(x86reg, 3, sign); - return 0; - - case 0x1f801488: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[3].target, sign); - return 0; - - // counters[4] - case 0x1f801490: - PUSH32I(4); - CONSTREAD16_CALL(psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f801494: - psxConstReadCounterMode16(x86reg, 4, sign); - return 0; - - case 0x1f801498: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[4].target, sign); - return 0; - - // counters[5] - case 0x1f8014a0: - PUSH32I(5); - CONSTREAD16_CALL(psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f8014a4: - psxConstReadCounterMode16(x86reg, 5, sign); - return 0; - - case 0x1f8014a8: - _eeReadConstMem16(x86reg, (uptr)&psxCounters[5].target, sign); - return 0; - - default: - if (add>=0x1f801c00 && add<0x1f801e00) { - - PUSH32I(add); - CONSTREAD16_CALL(SPU2read); - return 1; - } else { - _eeReadConstMem16(x86reg, (uptr)&psxH[(add) & 0xffff], sign); - return 0; - } - } -} - -void psxConstReadCounterMode32(int x86reg, int index) -{ - if( IS_MMXREG(x86reg) ) { - MMXONLY(MOV16MtoR(ECX, (uptr)&psxCounters[index].mode); - MOVDMtoMMX(x86reg&0xf, (uptr)&psxCounters[index].mode);) - } - else { - MOVZX32M16toR(ECX, (uptr)&psxCounters[index].mode); - MOV32RtoR(x86reg, ECX); - } - - //AND16ItoR(ECX, ~0x1800); - //OR16ItoR(ECX, 0x400); - //MOV16RtoM((uptr)&psxCounters[index].mode, ECX); -} - -static u32 s_tempsio; -int psxHwConstRead32(u32 x86reg, u32 add) { - if (add >= 0x1f801600 && add < 0x1f801700) { - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)USBread32); - return 1; - } - if (add >= 0x1f808400 && add <= 0x1f808550) {//the size is a complete guess.. - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)FWread32); - return 1; - } - - switch (add) { - case 0x1f801040: - iFlushCall(0); - CALLFunc((uptr)sioRead8); - AND32ItoR(EAX, 0xff); - MOV32RtoM((uptr)&s_tempsio, EAX); - CALLFunc((uptr)sioRead8); - AND32ItoR(EAX, 0xff); - SHL32ItoR(EAX, 8); - OR32RtoM((uptr)&s_tempsio, EAX); - - // 3rd - CALLFunc((uptr)sioRead8); - AND32ItoR(EAX, 0xff); - SHL32ItoR(EAX, 16); - OR32RtoM((uptr)&s_tempsio, EAX); - - // 4th - CALLFunc((uptr)sioRead8); - SHL32ItoR(EAX, 24); - OR32MtoR(EAX, (uptr)&s_tempsio); - return 1; - - //case 0x1f801050: hard = serial_read32(); break;//serial port - case 0x1f801078: - PSXHW_LOG("ICTRL 32bit read %x\n", psxHu32(0x1078)); - _eeReadConstMem32(x86reg, (uptr)&psxH[add&0xffff]); - MOV32ItoM((uptr)&psxH[add&0xffff], 0); - return 0; - - // counters[0] - case 0x1f801100: - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801104: - psxConstReadCounterMode32(x86reg, 0); - return 0; - - case 0x1f801108: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[0].target); - return 0; - - // counters[1] - case 0x1f801110: - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801114: - psxConstReadCounterMode32(x86reg, 1); - return 0; - - case 0x1f801118: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[1].target); - return 0; - - // counters[2] - case 0x1f801120: - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)psxRcntRcount16); - ADD32ItoR(ESP, 4); - return 1; - case 0x1f801124: - psxConstReadCounterMode32(x86reg, 2); - return 0; - - case 0x1f801128: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[2].target); - return 0; - - // counters[3] - case 0x1f801480: - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f801484: - psxConstReadCounterMode32(x86reg, 3); - return 0; - - case 0x1f801488: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[3].target); - return 0; - - // counters[4] - case 0x1f801490: - iFlushCall(0); - PUSH32I(4); - CALLFunc((uptr)psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f801494: - psxConstReadCounterMode32(x86reg, 4); - return 0; - - case 0x1f801498: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[4].target); - return 0; - - // counters[5] - case 0x1f8014a0: - iFlushCall(0); - PUSH32I(5); - CALLFunc((uptr)psxRcntRcount32); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1f8014a4: - psxConstReadCounterMode32(x86reg, 5); - return 0; - - case 0x1f8014a8: - _eeReadConstMem32(x86reg, (uptr)&psxCounters[5].target); - return 0; - - case 0x1F808200: - case 0x1F808204: - case 0x1F808208: - case 0x1F80820C: - case 0x1F808210: - case 0x1F808214: - case 0x1F808218: - case 0x1F80821C: - case 0x1F808220: - case 0x1F808224: - case 0x1F808228: - case 0x1F80822C: - case 0x1F808230: - case 0x1F808234: - case 0x1F808238: - case 0x1F80823C: - iFlushCall(0); - PUSH32I((add-0x1F808200)/4); - CALLFunc((uptr)sio2_getSend3); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1F808240: - case 0x1F808248: - case 0x1F808250: - case 0x1F80825C: - iFlushCall(0); - PUSH32I((add-0x1F808240)/8); - CALLFunc((uptr)sio2_getSend1); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1F808244: - case 0x1F80824C: - case 0x1F808254: - case 0x1F808258: - iFlushCall(0); - PUSH32I((add-0x1F808244)/8); - CALLFunc((uptr)sio2_getSend2); - ADD32ItoR(ESP, 4); - return 1; - - case 0x1F808268: - iFlushCall(0); - CALLFunc((uptr)sio2_getCtrl); - return 1; - - case 0x1F80826C: - iFlushCall(0); - CALLFunc((uptr)sio2_getRecv1); - return 1; - - case 0x1F808270: - iFlushCall(0); - CALLFunc((uptr)sio2_getRecv2); - return 1; - - case 0x1F808274: - iFlushCall(0); - CALLFunc((uptr)sio2_getRecv3); - return 1; - - case 0x1F808278: - iFlushCall(0); - CALLFunc((uptr)sio2_get8278); - return 1; - - case 0x1F80827C: - iFlushCall(0); - CALLFunc((uptr)sio2_get827C); - return 1; - - case 0x1F808280: - iFlushCall(0); - CALLFunc((uptr)sio2_getIntr); - return 1; - - case 0x1F801C00: - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)SPU2ReadMemAddr); - return 1; - - case 0x1F801500: - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)SPU2ReadMemAddr); - return 1; - - default: - _eeReadConstMem32(x86reg, (uptr)&psxH[(add) & 0xffff]); - return 0; - } -} - -#define CONSTWRITE_CALL(name) { \ - _recPushReg(mmreg); \ - iFlushCall(0); \ - CALLFunc((uptr)name); \ - ADD32ItoR(ESP, 4); \ -} \ - -void Write8PrintBuffer(u8 value) -{ - if (value == '\r') return; - if (value == '\n' || g_pbufi >= 1023) { - g_pbuf[g_pbufi++] = 0; g_pbufi = 0; - SysPrintf("%s\n", g_pbuf); return; - } - g_pbuf[g_pbufi++] = value; -} - -void psxHwConstWrite8(u32 add, int mmreg) -{ - if (add >= 0x1f801600 && add < 0x1f801700) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)USBwrite8); - return; - } - - switch (add) { - case 0x1f801040: - CONSTWRITE_CALL(sioWrite8); break; - //case 0x1f801050: serial_write8(value); break;//serial port - case 0x1f801100: - case 0x1f801104: - case 0x1f801108: - case 0x1f801110: - case 0x1f801114: - case 0x1f801118: - case 0x1f801120: - case 0x1f801124: - case 0x1f801128: - case 0x1f801480: - case 0x1f801484: - case 0x1f801488: - case 0x1f801490: - case 0x1f801494: - case 0x1f801498: - case 0x1f8014a0: - case 0x1f8014a4: - case 0x1f8014a8: - SysPrintf("8bit counter write %x\n", add); - _eeWriteConstMem8((uptr)&psxH[(add) & 0xffff], mmreg); - return; - case 0x1f801800: CONSTWRITE_CALL(cdrWrite0); break; - case 0x1f801801: CONSTWRITE_CALL(cdrWrite1); break; - case 0x1f801802: CONSTWRITE_CALL(cdrWrite2); break; - case 0x1f801803: CONSTWRITE_CALL(cdrWrite3); break; - case 0x1f80380c: CONSTWRITE_CALL(Write8PrintBuffer); break; - case 0x1F808260: CONSTWRITE_CALL(sio2_serialIn); break; - - default: - _eeWriteConstMem8((uptr)&psxH[(add) & 0xffff], mmreg); - return; - } -} - -void psxHwConstWrite16(u32 add, int mmreg) { - if (add >= 0x1f801600 && add < 0x1f801700) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)USBwrite16); - return; - } - - switch (add) { - case 0x1f801040: - _recPushReg(mmreg); - iFlushCall(0); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 1); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 3); - return; - case 0x1f801044: - return; - case 0x1f801048: - _eeWriteConstMem16((uptr)&sio.ModeReg, mmreg); - return; - case 0x1f80104a: // control register - CONSTWRITE_CALL(sioWriteCtrl16); - return; - case 0x1f80104e: // baudrate register - _eeWriteConstMem16((uptr)&sio.BaudReg, mmreg); - return; - - case 0x1f801070: - _eeWriteConstMem16OP((uptr)&psxHu32(0x1070), mmreg, 0); // AND operation - return; - - case 0x1f801074: - _eeWriteConstMem16((uptr)&psxHu32(0x1074), mmreg); - iFlushCall(0); - CALLFunc( (uptr)&iopTestIntc ); - return; - - case 0x1f801078: - //According to pSXAuthor this allways becomes 1 on write, but MHPB won't boot if value is not writen ;p - _eeWriteConstMem16((uptr)&psxHu32(0x1078), mmreg); - iFlushCall(0); - CALLFunc( (uptr)&iopTestIntc ); - return; - - // counters[0] - case 0x1f801100: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - case 0x1f801104: - CONSTWRITE_CALL(psxRcnt0Wmode); - return; - case 0x1f801108: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[1] - case 0x1f801110: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801114: - CONSTWRITE_CALL(psxRcnt1Wmode); - return; - - case 0x1f801118: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[2] - case 0x1f801120: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801124: - CONSTWRITE_CALL(psxRcnt2Wmode); - return; - - case 0x1f801128: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[3] - case 0x1f801480: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801484: - CONSTWRITE_CALL(psxRcnt3Wmode); - return; - - case 0x1f801488: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - // counters[4] - case 0x1f801490: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(4); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801494: - CONSTWRITE_CALL(psxRcnt4Wmode); - return; - - case 0x1f801498: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(4); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - // counters[5] - case 0x1f8014a0: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(5); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f8014a4: - CONSTWRITE_CALL(psxRcnt5Wmode); - return; - - case 0x1f8014a8: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(5); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - default: - if (add>=0x1f801c00 && add<0x1f801e00) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)SPU2write); - // leave esp alone - return; - } - - _eeWriteConstMem16((uptr)&psxH[(add) & 0xffff], mmreg); - return; - } -} - -#define recDmaExec(n) { \ - iFlushCall(0); \ - if( n > 6 ) TEST32ItoM((uptr)&HW_DMA_PCR2, 8 << (((n<<2)-28)&0x1f)); \ - else TEST32ItoM((uptr)&HW_DMA_PCR, 8 << (((n<<2))&0x1f)); \ - j8Ptr[5] = JZ8(0); \ - MOV32MtoR(EAX, (uptr)&HW_DMA##n##_CHCR); \ - TEST32ItoR(EAX, 0x01000000); \ - j8Ptr[6] = JZ8(0); \ - \ - _callFunctionArg3((uptr)psxDma##n, MEM_MEMORYTAG, MEM_MEMORYTAG, MEM_X86TAG, (uptr)&HW_DMA##n##_MADR, (uptr)&HW_DMA##n##_BCR, EAX); \ - \ - x86SetJ8( j8Ptr[5] ); \ - x86SetJ8( j8Ptr[6] ); \ -} \ - -#define CONSTWRITE_CALL32(name) { \ - iFlushCall(0); \ - _recPushReg(mmreg); \ - CALLFunc((uptr)name); \ - ADD32ItoR(ESP, 4); \ -} \ - -void psxHwConstWrite32(u32 add, int mmreg) -{ - if (add >= 0x1f801600 && add < 0x1f801700) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)USBwrite32); - return; - } - if (add >= 0x1f808400 && add <= 0x1f808550) { - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(add); - CALLFunc((uptr)FWwrite32); - return; - } - - switch (add) { - case 0x1f801040: - _recPushReg(mmreg); - iFlushCall(0); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 1); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 1); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 1); - CALLFunc((uptr)sioWrite8); - ADD32ItoR(ESP, 1); - return; - - case 0x1f801070: - _eeWriteConstMem32OP((uptr)&psxHu32(0x1070), mmreg, 0); // and - return; - - case 0x1f801074: - _eeWriteConstMem32((uptr)&psxHu32(0x1074), mmreg); - iFlushCall(0); - CALLFunc( (uptr)&iopTestIntc ); - return; - - case 0x1f801078: - //According to pSXAuthor this allways becomes 1 on write, but MHPB won't boot if value is not writen ;p - _eeWriteConstMem32((uptr)&psxHu32(0x1078), mmreg); - iFlushCall(0); - CALLFunc( (uptr)&iopTestIntc ); - return; - -// case 0x1f801088: -// HW_DMA0_CHCR = value; // DMA0 chcr (MDEC in DMA) -//// DmaExec(0); -// return; - -// case 0x1f801098: -// HW_DMA1_CHCR = value; // DMA1 chcr (MDEC out DMA) -//// DmaExec(1); -// return; - - case 0x1f8010a8: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(2); - return; - - case 0x1f8010b8: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(3); - return; - - case 0x1f8010c8: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(4); - return; - - case 0x1f8010e8: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(6); - return; - - case 0x1f801508: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(7); - return; - - case 0x1f801518: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(8); - return; - - case 0x1f801528: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(9); - return; - - case 0x1f801538: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(10); - return; - - case 0x1f801548: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(11); - return; - - case 0x1f801558: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - recDmaExec(12); - return; - - case 0x1f8010f4: - case 0x1f801574: - { - // u32 tmp = (~value) & HW_DMA_ICR; - _eeMoveMMREGtoR(EAX, mmreg); - MOV32RtoR(ECX, EAX); - NOT32R(ECX); - AND32MtoR(ECX, (uptr)&psxH[(add) & 0xffff]); - - // HW_DMA_ICR = ((tmp ^ value) & 0xffffff) ^ tmp; - XOR32RtoR(EAX, ECX); - AND32ItoR(EAX, 0xffffff); - XOR32RtoR(EAX, ECX); - MOV32RtoM((uptr)&psxH[(add) & 0xffff], EAX); - return; - } - - // counters[0] - case 0x1f801100: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - case 0x1f801104: - CONSTWRITE_CALL32(psxRcnt0Wmode); - return; - case 0x1f801108: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[1] - case 0x1f801110: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801114: - CONSTWRITE_CALL32(psxRcnt1Wmode); - return; - - case 0x1f801118: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[2] - case 0x1f801120: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)psxRcntWcount16); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801124: - CONSTWRITE_CALL32(psxRcnt2Wmode); - return; - - case 0x1f801128: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(2); - CALLFunc((uptr)psxRcntWtarget16); - ADD32ItoR(ESP, 8); - return; - - // counters[3] - case 0x1f801480: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801484: - CONSTWRITE_CALL32(psxRcnt3Wmode); - return; - - case 0x1f801488: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(3); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - // counters[4] - case 0x1f801490: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(4); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f801494: - CONSTWRITE_CALL32(psxRcnt4Wmode); - return; - - case 0x1f801498: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(4); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - // counters[5] - case 0x1f8014a0: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(5); - CALLFunc((uptr)psxRcntWcount32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f8014a4: - CONSTWRITE_CALL32(psxRcnt5Wmode); - return; - - case 0x1f8014a8: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(5); - CALLFunc((uptr)psxRcntWtarget32); - ADD32ItoR(ESP, 8); - return; - - case 0x1f8014c0: - SysPrintf("RTC_HOLDMODE 32bit write\n"); - break; - - case 0x1F808200: - case 0x1F808204: - case 0x1F808208: - case 0x1F80820C: - case 0x1F808210: - case 0x1F808214: - case 0x1F808218: - case 0x1F80821C: - case 0x1F808220: - case 0x1F808224: - case 0x1F808228: - case 0x1F80822C: - case 0x1F808230: - case 0x1F808234: - case 0x1F808238: - case 0x1F80823C: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I((add-0x1F808200)/4); - CALLFunc((uptr)sio2_setSend3); - ADD32ItoR(ESP, 8); - return; - - case 0x1F808240: - case 0x1F808248: - case 0x1F808250: - case 0x1F808258: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I((add-0x1F808240)/8); - CALLFunc((uptr)sio2_setSend1); - ADD32ItoR(ESP, 8); - return; - - case 0x1F808244: - case 0x1F80824C: - case 0x1F808254: - case 0x1F80825C: - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I((add-0x1F808244)/8); - CALLFunc((uptr)sio2_setSend2); - ADD32ItoR(ESP, 8); - return; - - case 0x1F808268: CONSTWRITE_CALL32(sio2_setCtrl); return; - case 0x1F808278: CONSTWRITE_CALL32(sio2_set8278); return; - case 0x1F80827C: CONSTWRITE_CALL32(sio2_set827C); return; - case 0x1F808280: CONSTWRITE_CALL32(sio2_setIntr); return; - - case 0x1F8010C0: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(0); - CALLFunc((uptr)SPU2WriteMemAddr); - return; - - case 0x1F801500: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - _recPushReg(mmreg); - iFlushCall(0); - PUSH32I(1); - CALLFunc((uptr)SPU2WriteMemAddr); - return; - default: - _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); - return; - } -} - -int psxHw4ConstRead8(u32 x86reg, u32 add, u32 sign) { - switch (add) { - case 0x1f402004: CONSTREAD8_CALL((uptr)cdvdRead04); return 1; - case 0x1f402005: CONSTREAD8_CALL((uptr)cdvdRead05); return 1; - case 0x1f402006: CONSTREAD8_CALL((uptr)cdvdRead06); return 1; - case 0x1f402007: CONSTREAD8_CALL((uptr)cdvdRead07); return 1; - case 0x1f402008: CONSTREAD8_CALL((uptr)cdvdRead08); return 1; - case 0x1f40200A: CONSTREAD8_CALL((uptr)cdvdRead0A); return 1; - case 0x1f40200B: CONSTREAD8_CALL((uptr)cdvdRead0B); return 1; - case 0x1f40200C: CONSTREAD8_CALL((uptr)cdvdRead0C); return 1; - case 0x1f40200D: CONSTREAD8_CALL((uptr)cdvdRead0D); return 1; - case 0x1f40200E: CONSTREAD8_CALL((uptr)cdvdRead0E); return 1; - case 0x1f40200F: CONSTREAD8_CALL((uptr)cdvdRead0F); return 1; - case 0x1f402013: CONSTREAD8_CALL((uptr)cdvdRead13); return 1; - case 0x1f402015: CONSTREAD8_CALL((uptr)cdvdRead15); return 1; - case 0x1f402016: CONSTREAD8_CALL((uptr)cdvdRead16); return 1; - case 0x1f402017: CONSTREAD8_CALL((uptr)cdvdRead17); return 1; - case 0x1f402018: CONSTREAD8_CALL((uptr)cdvdRead18); return 1; - case 0x1f402020: CONSTREAD8_CALL((uptr)cdvdRead20); return 1; - case 0x1f402021: CONSTREAD8_CALL((uptr)cdvdRead21); return 1; - case 0x1f402022: CONSTREAD8_CALL((uptr)cdvdRead22); return 1; - case 0x1f402023: CONSTREAD8_CALL((uptr)cdvdRead23); return 1; - case 0x1f402024: CONSTREAD8_CALL((uptr)cdvdRead24); return 1; - case 0x1f402028: CONSTREAD8_CALL((uptr)cdvdRead28); return 1; - case 0x1f402029: CONSTREAD8_CALL((uptr)cdvdRead29); return 1; - case 0x1f40202A: CONSTREAD8_CALL((uptr)cdvdRead2A); return 1; - case 0x1f40202B: CONSTREAD8_CALL((uptr)cdvdRead2B); return 1; - case 0x1f40202C: CONSTREAD8_CALL((uptr)cdvdRead2C); return 1; - case 0x1f402030: CONSTREAD8_CALL((uptr)cdvdRead30); return 1; - case 0x1f402031: CONSTREAD8_CALL((uptr)cdvdRead31); return 1; - case 0x1f402032: CONSTREAD8_CALL((uptr)cdvdRead32); return 1; - case 0x1f402033: CONSTREAD8_CALL((uptr)cdvdRead33); return 1; - case 0x1f402034: CONSTREAD8_CALL((uptr)cdvdRead34); return 1; - case 0x1f402038: CONSTREAD8_CALL((uptr)cdvdRead38); return 1; - case 0x1f402039: CONSTREAD8_CALL((uptr)cdvdRead39); return 1; - case 0x1f40203A: CONSTREAD8_CALL((uptr)cdvdRead3A); return 1; - default: - Console::Notice("*Unknown 8bit read at address %lx", params add); - XOR32RtoR(x86reg, x86reg); - return 0; - } -} - -void psxHw4ConstWrite8(u32 add, int mmreg) { - switch (add) { - case 0x1f402004: CONSTWRITE_CALL(cdvdWrite04); return; - case 0x1f402005: CONSTWRITE_CALL(cdvdWrite05); return; - case 0x1f402006: CONSTWRITE_CALL(cdvdWrite06); return; - case 0x1f402007: CONSTWRITE_CALL(cdvdWrite07); return; - case 0x1f402008: CONSTWRITE_CALL(cdvdWrite08); return; - case 0x1f40200A: CONSTWRITE_CALL(cdvdWrite0A); return; - case 0x1f40200F: CONSTWRITE_CALL(cdvdWrite0F); return; - case 0x1f402014: CONSTWRITE_CALL(cdvdWrite14); return; - case 0x1f402016: - MMXONLY(_freeMMXregs();) - CONSTWRITE_CALL(cdvdWrite16); - return; - case 0x1f402017: CONSTWRITE_CALL(cdvdWrite17); return; - case 0x1f402018: CONSTWRITE_CALL(cdvdWrite18); return; - case 0x1f40203A: CONSTWRITE_CALL(cdvdWrite3A); return; - default: - Console::Notice("*Unknown 8bit write at address %lx", params add); - return; - } -} diff --git a/pcsx2/x86/iR5900CoissuedLoadStore.cpp b/pcsx2/x86/iR5900CoissuedLoadStore.cpp deleted file mode 100644 index 7ca346bc9a..0000000000 --- a/pcsx2/x86/iR5900CoissuedLoadStore.cpp +++ /dev/null @@ -1,1738 +0,0 @@ -/* Pcsx2 - Pc Ps2 Emulator -* Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" - -#ifdef PCSX2_VM_COISSUE - -#include "Common.h" -#include "R5900OpcodeTables.h" -#include "iR5900LoadStore.h" -#include "iR5900.h" - -namespace R5900 { -namespace Dynarec { -namespace OpcodeImpl { - -#define _Imm_co_ (*(s16*)PSM(pc)) - -int _eePrepareReg_coX(int gprreg, int num) -{ - int mmreg = _eePrepareReg(gprreg); - - if( (mmreg&MEM_MMXTAG) && num == 7 ) { - if( mmxregs[mmreg&0xf].mode & MODE_WRITE ) { - MOVQRtoM((u32)&cpuRegs.GPR.r[gprreg], mmreg&0xf); - mmxregs[mmreg&0xf].mode &= ~MODE_WRITE; - mmxregs[mmreg&0xf].needed = 0; - } - } - - return mmreg; -} - -void recLoad32_co(u32 bit, u32 sign) -{ - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - int mmreg1 = -1, mmreg2 = -1; - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - int ineax = 0; - u32 written = 0; - - _eeOnLoadWrite(_Rt_); - _eeOnLoadWrite(nextrt); - - if( bit == 32 ) { - mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg1 >= 0 ) mmreg1 |= MEM_MMXTAG; - else mmreg1 = EBX; - - mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); - if( mmreg2 >= 0 ) mmreg2 |= MEM_MMXTAG; - else mmreg2 = EAX; - } - else { - _deleteEEreg(_Rt_, 0); - _deleteEEreg(nextrt, 0); - mmreg1 = EBX; - mmreg2 = EAX; - } - - // do const processing - switch(bit) { - case 8: - if( recMemConstRead8(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_, sign) ) { - if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - written = 1; - } - ineax = recMemConstRead8(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, sign); - break; - case 16: - if( recMemConstRead16(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_, sign) ) { - if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - written = 1; - } - ineax = recMemConstRead16(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, sign); - break; - case 32: - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); - if( recMemConstRead32(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { - if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - written = 1; - } - ineax = recMemConstRead32(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_); - break; - } - - if( !written && _Rt_ ) { - if( mmreg1&MEM_MMXTAG ) { - assert( mmxregs[mmreg1&0xf].mode & MODE_WRITE ); - if( sign ) _signExtendGPRtoMMX(mmreg1&0xf, _Rt_, 32-bit); - else if( bit < 32 ) PSRLDItoR(mmreg1&0xf, 32-bit); - } - else recTransferX86ToReg(mmreg1, _Rt_, sign); - } - if( nextrt ) { - g_pCurInstInfo++; - if( !ineax && (mmreg2 & MEM_MMXTAG) ) { - assert( mmxregs[mmreg2&0xf].mode & MODE_WRITE ); - if( sign ) _signExtendGPRtoMMX(mmreg2&0xf, nextrt, 32-bit); - else if( bit < 32 ) PSRLDItoR(mmreg2&0xf, 32-bit); - } - else { - if( mmreg2&MEM_MMXTAG ) mmxregs[mmreg2&0xf].inuse = 0; - recTransferX86ToReg(mmreg2, nextrt, sign); - } - g_pCurInstInfo--; - } - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg(_Rs_); - assert( !REC_FORCEMMX ); - - _eeOnLoadWrite(_Rt_); - _eeOnLoadWrite(nextrt); - _deleteEEreg(_Rt_, 0); - _deleteEEreg(nextrt, 0); - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); - - switch(bit) { - case 8: - if( sign ) { - MOVSX32Rm8toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVSX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - MOVZX32Rm8toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVZX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - break; - case 16: - if( sign ) { - MOVSX32Rm16toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVSX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - MOVZX32Rm16toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVZX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - break; - case 32: - MOV32RmtoROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - break; - } - - if ( _Rt_ ) recTransferX86ToReg(EBX, _Rt_, sign); - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - switch(bit) { - case 8: - MOV32RtoM((u32)&s_tempaddr, ECX); - CALLFunc( (int)recMemRead8 ); - if( sign ) MOVSX32R8toR(EAX, EAX); - MOV32MtoR(ECX, (u32)&s_tempaddr); - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead8 ); - if( sign ) MOVSX32R8toR(EAX, EAX); - break; - case 16: - MOV32RtoM((u32)&s_tempaddr, ECX); - CALLFunc( (int)recMemRead16 ); - if( sign ) MOVSX32R16toR(EAX, EAX); - MOV32MtoR(ECX, (u32)&s_tempaddr); - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead16 ); - if( sign ) MOVSX32R16toR(EAX, EAX); - break; - case 32: - MOV32RtoM((u32)&s_tempaddr, ECX); - iMemRead32Check(); - CALLFunc( (int)recMemRead32 ); - MOV32MtoR(ECX, (u32)&s_tempaddr); - if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); - - ADD32ItoR(ECX, _Imm_co_-_Imm_); - iMemRead32Check(); - CALLFunc( (int)recMemRead32 ); - break; - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); - else x86SetJ8(j8Ptr[2]); - } - - if( nextrt ) { - g_pCurInstInfo++; - recTransferX86ToReg(EAX, nextrt, sign); - g_pCurInstInfo--; - } - } -} - -void recLB_co( void ) { recLoad32_co(8, 1); } -void recLBU_co( void ) { recLoad32_co(8, 0); } -void recLH_co( void ) { recLoad32_co(16, 1); } -void recLHU_co( void ) { recLoad32_co(16, 0); } -void recLW_co( void ) { recLoad32_co(32, 1); } -void recLWU_co( void ) { recLoad32_co(32, 0); } -void recLDL_co(void) { recLoad64(_Imm_-7, 0); } -void recLDR_co(void) { recLoad64(_Imm_, 0); } -void recLWL_co(void) { recLoad32(32, _Imm_-3, 1); } // paired with LWR -void recLWR_co(void) { recLoad32(32, _Imm_, 1); } // paired with LWL - -void recLD_co( void ) -{ -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - recLD(); - g_pCurInstInfo++; - cpuRegs.code = *(u32*)PSM(pc); - recLD(); - g_pCurInstInfo--; // incremented later - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg(_Rs_); - int mmreg1 = -1, mmreg2 = -1, t0reg = -1, t1reg = -1; - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - - if( _Rt_ ) _eeOnWriteReg(_Rt_, 0); - if( nextrt ) _eeOnWriteReg(nextrt, 0); - - if( _Rt_ ) { - mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg1 < 0 ) { - mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ); - if( mmreg1 >= 0 ) mmreg1 |= 0x8000; - } - - if( mmreg1 < 0 && _hasFreeMMXreg() ) mmreg1 = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); - } - - if( nextrt ) { - mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); - if( mmreg2 < 0 ) { - mmreg2 = _allocCheckGPRtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE|MODE_READ); - if( mmreg2 >= 0 ) mmreg2 |= 0x8000; - } - - if( mmreg2 < 0 && _hasFreeMMXreg() ) mmreg2 = _allocMMXreg(-1, MMX_GPR+nextrt, MODE_WRITE); - } - - if( mmreg1 < 0 || mmreg2 < 0 ) { - t0reg = _allocMMXreg(-1, MMX_TEMP, 0); - - if( mmreg1 < 0 && mmreg2 < 0 && _hasFreeMMXreg() ) { - t1reg = _allocMMXreg(-1, MMX_TEMP, 0); - } - else t1reg = t0reg; - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 1, 0); - - if( mmreg1 >= 0 ) { - if( mmreg1 & 0x8000 ) SSE_MOVLPSRmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); - else MOVQRmtoROffset(mmreg1, ECX, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - if( _Rt_ ) { - MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVQRtoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], t0reg); - } - } - - if( mmreg2 >= 0 ) { - if( mmreg2 & 0x8000 ) SSE_MOVLPSRmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - else MOVQRmtoROffset(mmreg2, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - if( nextrt ) { - MOVQRmtoROffset(t1reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOVQRtoM((int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ], t1reg); - } - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - if( mmreg1 >= 0 ) { - PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); - CALLFunc( (int)recMemRead64 ); - - if( mmreg1 & 0x8000 ) SSE_MOVLPS_M64_to_XMM(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); - else MOVQMtoR(mmreg1, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); - } - else { - if( _Rt_ ) { - _deleteEEreg(_Rt_, 0); - PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); - } - else PUSH32I( (int)&retValues[0] ); - - CALLFunc( (int)recMemRead64 ); - } - - MOV32MtoR(ECX, (u32)&s_tempaddr); - - if( mmreg2 >= 0 ) { - MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ], 0 ); - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead64 ); - - if( mmreg2 & 0x8000 ) SSE_MOVLPS_M64_to_XMM(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ]); - else MOVQMtoR(mmreg2, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ]); - } - else { - if( nextrt ) { - _deleteEEreg(nextrt, 0); - MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ], 0 ); - } - else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0 ); - - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead64 ); - } - - ADD32ItoR(ESP, 4); - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - - if( mmreg1 < 0 || mmreg2 < 0 || !(mmreg1&0x8000) || !(mmreg2&0x8000) ) SetMMXstate(); - - if( t0reg >= 0 ) _freeMMXreg(t0reg); - if( t0reg != t1reg && t1reg >= 0 ) _freeMMXreg(t1reg); - - _clearNeededMMXregs(); - _clearNeededXMMregs(); - } -} - -void recLD_coX( int num ) -{ - int i; - int mmreg = -1; - int mmregs[XMMREGS]; - int nextrts[XMMREGS]; - - assert( num > 1 && num < XMMREGS ); - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - recLD(); - - for(i = 0; i < num; ++i) { - g_pCurInstInfo++; - cpuRegs.code = *(u32*)PSM(pc+i*4); - recLD(); - } - - g_pCurInstInfo -= num; // incremented later - } - else -#endif - { - int dohw; - int mmregS = _eePrepareReg_coX(_Rs_, num); - - if( _Rt_ ) _eeOnWriteReg(_Rt_, 0); - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - _eeOnWriteReg(nextrts[i], 0); - } - - if( _Rt_ ) { - mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg < 0 ) { - mmreg = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; - else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE)|MEM_MMXTAG; - } - else mmreg |= MEM_MMXTAG; - } - - for(i = 0; i < num; ++i) { - mmregs[i] = _allocCheckGPRtoMMX(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE); - if( mmregs[i] < 0 ) { - mmregs[i] = _allocCheckGPRtoXMM(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE|MODE_READ); - if( mmregs[i] >= 0 ) mmregs[i] |= MEM_XMMTAG; - else mmregs[i] = _allocMMXreg(-1, MMX_GPR+nextrts[i], MODE_WRITE)|MEM_MMXTAG; - } - else mmregs[i] |= MEM_MMXTAG; - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 1, 0); - - if( mmreg >= 0 ) { - if( mmreg & MEM_XMMTAG ) SSE_MOVLPSRmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); - else MOVQRmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); - } - - for(i = 0; i < num; ++i) { - u32 off = PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_; - - if( mmregs[i] >= 0 ) { - if( mmregs[i] & MEM_XMMTAG ) SSE_MOVLPSRmtoROffset(mmregs[i]&0xf, ECX, off); - else MOVQRmtoROffset(mmregs[i]&0xf, ECX, off); - } - } - - if( dohw ) { - if( (s_bCachingMem & 2) || num > 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - if( mmreg >= 0 ) { - PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); - CALLFunc( (int)recMemRead64 ); - - if( mmreg & MEM_XMMTAG ) SSE_MOVLPS_M64_to_XMM(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); - else MOVQMtoR(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); - } - else { - PUSH32I( (int)&retValues[0] ); - CALLFunc( (int)recMemRead64 ); - } - - for(i = 0; i < num; ++i ) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); - - if( mmregs[i] >= 0 ) { - MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrts[i]].UL[0], 0); - CALLFunc( (int)recMemRead64 ); - - if( mmregs[i] & MEM_XMMTAG ) SSE_MOVLPS_M64_to_XMM(mmregs[i]&0xf, (int)&cpuRegs.GPR.r[ nextrts[i] ].UD[ 0 ]); - else MOVQMtoR(mmregs[i]&0xf, (int)&cpuRegs.GPR.r[ nextrts[i] ].UD[ 0 ]); - } - else { - MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); - CALLFunc( (int)recMemRead64 ); - } - } - - ADD32ItoR(ESP, 4); - - if( (s_bCachingMem & 2) || num > 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - - _clearNeededMMXregs(); - _clearNeededXMMregs(); - } -} - -void recLQ_co( void ) -{ -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - recLQ(); - g_pCurInstInfo++; - cpuRegs.code = *(u32*)PSM(pc); - recLQ(); - g_pCurInstInfo--; // incremented later - } - else -#endif - { - int dohw; - int t0reg = -1; - int mmregs = _eePrepareReg(_Rs_); - - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - int mmreg1 = -1, mmreg2 = -1; - - if( _Rt_ ) { - _eeOnWriteReg(_Rt_, 0); - - if( _hasFreeMMXreg() ) { - mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg1 >= 0 ) { - mmreg1 |= MEM_MMXTAG; - if( t0reg < 0 ) t0reg = _allocMMXreg(-1, MMX_TEMP, 0); - } - } - else _deleteMMXreg(MMX_GPR+_Rt_, 2); - - if( mmreg1 < 0 ) { - mmreg1 = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); - if( mmreg1 >= 0 ) mmreg1 |= MEM_XMMTAG; - } - } - - if( nextrt ) { - _eeOnWriteReg(nextrt, 0); - - if( _hasFreeMMXreg() ) { - mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); - if( mmreg2 >= 0 ) { - mmreg2 |= MEM_MMXTAG; - if( t0reg < 0 ) t0reg = _allocMMXreg(-1, MMX_TEMP, 0); - } - } - else _deleteMMXreg(MMX_GPR+nextrt, 2); - - if( mmreg2 < 0 ) { - mmreg2 = _allocGPRtoXMMreg(-1, nextrt, MODE_WRITE); - if( mmreg2 >= 0 ) mmreg2 |= MEM_XMMTAG; - } - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); - - if( _Rt_ ) { - if( mmreg1 >= 0 && (mmreg1 & MEM_MMXTAG) ) { - MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+8); - MOVQRmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UL[2], t0reg); - } - else if( mmreg1 >= 0 && (mmreg1 & MEM_XMMTAG) ) { - SSEX_MOVDQARmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - _recMove128RmOffsettoM((u32)&cpuRegs.GPR.r[_Rt_].UL[0], PS2MEM_BASE_+s_nAddMemOffset); - } - } - - if( nextrt ) { - if( mmreg2 >= 0 && (mmreg2 & MEM_MMXTAG) ) { - MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); - MOVQRmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOVQRtoM((u32)&cpuRegs.GPR.r[nextrt].UL[2], t0reg); - } - else if( mmreg2 >= 0 && (mmreg2 & MEM_XMMTAG) ) { - SSEX_MOVDQARmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - _recMove128RmOffsettoM((u32)&cpuRegs.GPR.r[nextrt].UL[0], PS2MEM_BASE_+s_nAddMemOffset); - } - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - if( _Rt_ ) PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); - else PUSH32I( (int)&retValues[0] ); - CALLFunc( (int)recMemRead128 ); - - MOV32MtoR(ECX, (u32)&s_tempaddr); - if( nextrt ) MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrt].UL[0], 0); - else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead128 ); - - if( _Rt_) { - if( mmreg1 >= 0 && (mmreg1 & MEM_MMXTAG) ) MOVQMtoR(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); - else if( mmreg1 >= 0 && (mmreg1 & MEM_XMMTAG) ) SSEX_MOVDQA_M128_to_XMM(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); - } - if( nextrt ) { - if( mmreg2 >= 0 && (mmreg2 & MEM_MMXTAG) ) MOVQMtoR(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ]); - else if( mmreg2 >= 0 && (mmreg2 & MEM_XMMTAG) ) SSEX_MOVDQA_M128_to_XMM(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ] ); - } - ADD32ItoR(ESP, 4); - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); - else x86SetJ8(j8Ptr[2]); - } - - if( t0reg >= 0 ) _freeMMXreg(t0reg); - } -} - -// coissues more than 2 LQs -void recLQ_coX(int num) -{ - int i; - int mmreg = -1; - int mmregs[XMMREGS]; - int nextrts[XMMREGS]; - - assert( num > 1 && num < XMMREGS ); - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - recLQ(); - - for(i = 0; i < num; ++i) { - g_pCurInstInfo++; - cpuRegs.code = *(u32*)PSM(pc+i*4); - recLQ(); - } - - g_pCurInstInfo -= num; // incremented later - } - else -#endif - { - int dohw; - int mmregS = _eePrepareReg_coX(_Rs_, num); - - - if( _Rt_ ) _deleteMMXreg(MMX_GPR+_Rt_, 2); - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - if( nextrts[i] ) _deleteMMXreg(MMX_GPR+nextrts[i], 2); - } - - if( _Rt_ ) { - _eeOnWriteReg(_Rt_, 0); - mmreg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); - } - - for(i = 0; i < num; ++i) { - if( nextrts[i] ) { - _eeOnWriteReg(nextrts[i], 0); - mmregs[i] = _allocGPRtoXMMreg(-1, nextrts[i], MODE_WRITE); - } - else mmregs[i] = -1; - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 2, 1); - - if( _Rt_ ) SSEX_MOVDQARmtoROffset(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); - - for(i = 0; i < num; ++i) { - u32 off = s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_; - if( nextrts[i] ) SSEX_MOVDQARmtoROffset(mmregs[i], ECX, PS2MEM_BASE_+off&~0xf); - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - if( _Rt_ ) PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); - else PUSH32I( (int)&retValues[0] ); - CALLFunc( (int)recMemRead128 ); - if( _Rt_) SSEX_MOVDQA_M128_to_XMM(mmreg, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); - - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); - - if( nextrts[i] ) MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrts[i]].UL[0], 0); - else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); - CALLFunc( (int)recMemRead128 ); - if( nextrts[i] ) SSEX_MOVDQA_M128_to_XMM(mmregs[i], (int)&cpuRegs.GPR.r[ nextrts[i] ].UL[ 0 ] ); - } - - ADD32ItoR(ESP, 4); - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - } -} - -void recStore_co(int bit, int align) -{ - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - u32 addr = g_cpuConstRegs[_Rs_].UL[0]+_Imm_; - u32 coaddr = g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_; - int mmreg, t0reg = -1, mmreg2; - int doclear = 0; - - switch(bit) { - case 8: - if( GPR_IS_CONST1(_Rt_) ) doclear |= recMemConstWrite8(addr, MEM_EECONSTTAG|(_Rt_<<16)); - else { - _eeMoveGPRtoR(EAX, _Rt_); - doclear |= recMemConstWrite8(addr, EAX); - } - if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite8(coaddr, MEM_EECONSTTAG|(nextrt<<16)); - else { - _eeMoveGPRtoR(EAX, nextrt); - doclear |= recMemConstWrite8(coaddr, EAX); - } - break; - case 16: - assert( (addr)%2 == 0 ); - assert( (coaddr)%2 == 0 ); - - if( GPR_IS_CONST1(_Rt_) ) doclear |= recMemConstWrite16(addr, MEM_EECONSTTAG|(_Rt_<<16)); - else { - _eeMoveGPRtoR(EAX, _Rt_); - doclear |= recMemConstWrite16(addr, EAX); - } - - if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite16(coaddr, MEM_EECONSTTAG|(nextrt<<16)); - else { - _eeMoveGPRtoR(EAX, nextrt); - doclear |= recMemConstWrite16(coaddr, EAX); - } - break; - case 32: - assert( (addr)%4 == 0 ); - if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite32(addr, MEM_EECONSTTAG|(_Rt_<<16)); - else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { - doclear = recMemConstWrite32(addr, mmreg|MEM_XMMTAG|(_Rt_<<16)); - } - else if( (mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ)) >= 0 ) { - doclear = recMemConstWrite32(addr, mmreg|MEM_MMXTAG|(_Rt_<<16)); - } - else { - _eeMoveGPRtoR(EAX, _Rt_); - doclear = recMemConstWrite32(addr, EAX); - } - - if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite32(coaddr, MEM_EECONSTTAG|(nextrt<<16)); - else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, nextrt, MODE_READ)) >= 0 ) { - doclear |= recMemConstWrite32(coaddr, mmreg|MEM_XMMTAG|(nextrt<<16)); - } - else if( (mmreg = _checkMMXreg(MMX_GPR+nextrt, MODE_READ)) >= 0 ) { - doclear |= recMemConstWrite32(coaddr, mmreg|MEM_MMXTAG|(nextrt<<16)); - } - else { - _eeMoveGPRtoR(EAX, nextrt); - doclear |= recMemConstWrite32(coaddr, EAX); - } - - break; - case 64: - { - int mask = align ? ~7 : ~0; - //assert( (addr)%8 == 0 ); - - if( GPR_IS_CONST1(_Rt_) ) mmreg = MEM_EECONSTTAG|(_Rt_<<16); - else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { - mmreg |= MEM_XMMTAG|(_Rt_<<16); - } - else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ)|MEM_MMXTAG|(_Rt_<<16); - - if( GPR_IS_CONST1(nextrt) ) mmreg2 = MEM_EECONSTTAG|(nextrt<<16); - else if( (mmreg2 = _checkXMMreg(XMMTYPE_GPRREG, nextrt, MODE_READ)) >= 0 ) { - mmreg2 |= MEM_XMMTAG|(nextrt<<16); - } - else mmreg2 = _allocMMXreg(-1, MMX_GPR+nextrt, MODE_READ)|MEM_MMXTAG|(nextrt<<16); - - doclear = recMemConstWrite64((addr)&mask, mmreg); - doclear |= recMemConstWrite64((coaddr)&mask, mmreg2); - doclear <<= 1; - break; - } - case 128: - assert( (addr)%16 == 0 ); - - mmreg = _eePrepConstWrite128(_Rt_); - mmreg2 = _eePrepConstWrite128(nextrt); - doclear = recMemConstWrite128((addr)&~15, mmreg); - doclear |= recMemConstWrite128((coaddr)&~15, mmreg2); - doclear <<= 2; - break; - } - - if( doclear ) { - u8* ptr; - CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+(_Imm_ < _Imm_co_ ? _Imm_ : _Imm_co_)); - ptr = JB8(0); - recMemConstClear((addr)&~(doclear*4-1), doclear); - recMemConstClear((coaddr)&~(doclear*4-1), doclear); - x86SetJ8A(ptr); - } - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg(_Rs_); - int off = _Imm_co_-_Imm_; - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, align ? bit/64 : 0, bit==128); - - recStore_raw(g_pCurInstInfo, bit, EAX, _Rt_, s_nAddMemOffset); - recStore_raw(g_pCurInstInfo+1, bit, EBX, nextrt, s_nAddMemOffset+off); - - // clear the writes, do only one camera (with the lowest addr) - if( off < 0 ) ADD32ItoR(ECX, s_nAddMemOffset+off); - else if( s_nAddMemOffset ) ADD32ItoR(ECX, s_nAddMemOffset); - CMP32MtoR(ECX, (u32)&maxrecmem); - - if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); - else j8Ptr[1] = JAE8(0); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - if( bit < 32 ) AND8ItoR(ECX, 0xfc); - if( bit <= 32 ) CALLFunc((uptr)recWriteMemClear32); - else if( bit == 64 ) CALLFunc((uptr)recWriteMemClear64); - else CALLFunc((uptr)recWriteMemClear128); - - MOV32MtoR(ECX, (u32)&s_tempaddr); - if( off < 0 ) ADD32ItoR(ECX, -off); - else ADD32ItoR(ECX, off); - - if( bit < 32 ) AND8ItoR(ECX, 0xfc); - if( bit <= 32 ) CALLFunc((uptr)recWriteMemClear32); - else if( bit == 64 ) CALLFunc((uptr)recWriteMemClear64); - else CALLFunc((uptr)recWriteMemClear128); - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - recStore_call(bit, _Rt_, s_nAddMemOffset); - MOV32MtoR(ECX, (u32)&s_tempaddr); - recStore_call(bit, nextrt, s_nAddMemOffset+_Imm_co_-_Imm_); - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); - else x86SetJ8(j8Ptr[2]); - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); - else x86SetJ8(j8Ptr[1]); - } - - _clearNeededMMXregs(); // needed since allocing - _clearNeededXMMregs(); // needed since allocing -} - -void recSB_co( void ) { recStore_co(8, 1); } -void recSH_co( void ) { recStore_co(16, 1); } -void recSW_co( void ) { recStore_co(32, 1); } -void recSD_co( void ) { recStore_co(64, 1); } -void recSQ_co( void ) { recStore_co(128, 1); } - -void recSWL_co(void) { recStore(32, _Imm_-3, 0); } -void recSWR_co(void) { recStore(32, _Imm_, 0); } -void recSDL_co(void) { recStore(64, _Imm_-7, 0); } -void recSDR_co(void) { recStore(64, _Imm_, 0); } - -// coissues more than 2 SDs -void recSD_coX(int num, int align) -{ - int i; - int mmreg = -1; - int nextrts[XMMREGS]; - u32 mask = align ? ~7 : ~0; - - assert( num > 1 && num < XMMREGS ); - - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - } - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - int minimm = _Imm_; - int t0reg = -1; - int doclear = 0; - - if( GPR_IS_CONST1(_Rt_) ) mmreg = MEM_EECONSTTAG|(_Rt_<<16); - else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { - mmreg |= MEM_XMMTAG|(_Rt_<<16); - } - else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ)|MEM_MMXTAG|(_Rt_<<16); - doclear |= recMemConstWrite64((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&mask, mmreg); - - for(i = 0; i < num; ++i) { - int imm = (*(s16*)PSM(pc+i*4)); - if( minimm > imm ) minimm = imm; - - if( GPR_IS_CONST1(nextrts[i]) ) mmreg = MEM_EECONSTTAG|(nextrts[i]<<16); - else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, nextrts[i], MODE_READ)) >= 0 ) { - mmreg |= MEM_XMMTAG|(nextrts[i]<<16); - } - else mmreg = _allocMMXreg(-1, MMX_GPR+nextrts[i], MODE_READ)|MEM_MMXTAG|(nextrts[i]<<16); - doclear |= recMemConstWrite64((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&mask, mmreg); - } - - if( doclear ) { - u32* ptr; - CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+minimm); - ptr = JB32(0); - recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~7, 4); - - for(i = 0; i < num; ++i) { - recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&~7, 2); - } - x86SetJ32A(ptr); - } - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg_coX(_Rs_, num); - int minoff = 0; - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, align, 1); - - recStore_raw(g_pCurInstInfo, 64, EAX, _Rt_, s_nAddMemOffset); - - for(i = 0; i < num; ++i) { - recStore_raw(g_pCurInstInfo+i+1, 64, EAX, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - - // clear the writes - minoff = _Imm_; - for(i = 0; i < num; ++i) { - if( minoff > (*(s16*)PSM(pc+i*4)) ) minoff = (*(s16*)PSM(pc+i*4)); - } - - if( s_nAddMemOffset || minoff != _Imm_ ) ADD32ItoR(ECX, s_nAddMemOffset+minoff-_Imm_); - CMP32MtoR(ECX, (u32)&maxrecmem); - if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); - else j8Ptr[1] = JAE8(0); - - MOV32RtoM((u32)&s_tempaddr, ECX); - if( minoff != _Imm_ ) ADD32ItoR(ECX, _Imm_-minoff); - CALLFunc((uptr)recWriteMemClear64); - - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - if( minoff != (*(s16*)PSM(pc+i*4)) ) ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-minoff); - CALLFunc((uptr)recWriteMemClear64); - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - recStore_call(64, _Rt_, s_nAddMemOffset); - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - recStore_call(64, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); - else x86SetJ8(j8Ptr[2]); - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); - else x86SetJ8(j8Ptr[1]); - - _clearNeededMMXregs(); // needed since allocing - _clearNeededXMMregs(); // needed since allocing - } -} - -// coissues more than 2 SQs -void recSQ_coX(int num) -{ - int i; - int mmreg = -1; - int nextrts[XMMREGS]; - - assert( num > 1 && num < XMMREGS ); - - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - } - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - int minimm = _Imm_; - int t0reg = -1; - int doclear = 0; - - mmreg = _eePrepConstWrite128(_Rt_); - doclear |= recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg); - - for(i = 0; i < num; ++i) { - int imm = (*(s16*)PSM(pc+i*4)); - if( minimm > imm ) minimm = imm; - - mmreg = _eePrepConstWrite128(nextrts[i]); - doclear |= recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+imm)&~15, mmreg); - } - - if( doclear ) { - u32* ptr; - CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+minimm); - ptr = JB32(0); - recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, 4); - - for(i = 0; i < num; ++i) { - recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&~15, 4); - } - x86SetJ32A(ptr); - } - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg_coX(_Rs_, num); - int minoff = 0; - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 1); - - recStore_raw(g_pCurInstInfo, 128, EAX, _Rt_, s_nAddMemOffset); - - for(i = 0; i < num; ++i) { - recStore_raw(g_pCurInstInfo+i+1, 128, EAX, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - - // clear the writes - minoff = _Imm_; - for(i = 0; i < num; ++i) { - if( minoff > (*(s16*)PSM(pc+i*4)) ) minoff = (*(s16*)PSM(pc+i*4)); - } - - if( s_nAddMemOffset || minoff != _Imm_ ) ADD32ItoR(ECX, s_nAddMemOffset+minoff-_Imm_); - CMP32MtoR(ECX, (u32)&maxrecmem); - if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); - else j8Ptr[1] = JAE8(0); - - MOV32RtoM((u32)&s_tempaddr, ECX); - if( minoff != _Imm_ ) ADD32ItoR(ECX, _Imm_-minoff); - CALLFunc((uptr)recWriteMemClear128); - - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - if( minoff != (*(s16*)PSM(pc+i*4)) ) ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-minoff); - CALLFunc((uptr)recWriteMemClear128); - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - recStore_call(128, _Rt_, s_nAddMemOffset); - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - recStore_call(128, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); - else x86SetJ8(j8Ptr[2]); - } - - if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); - else x86SetJ8(j8Ptr[1]); - - _clearNeededMMXregs(); // needed since allocing - _clearNeededXMMregs(); // needed since allocing - } -} - -void recLWC1_co( void ) -{ - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - u32 written = 0; - int ineax, mmreg1, mmreg2; - - mmreg1 = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg1 >= 0 ) mmreg1 |= MEM_XMMTAG; - else mmreg1 = EBX; - - mmreg2 = _allocCheckFPUtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE); - if( mmreg2 >= 0 ) mmreg2 |= MEM_XMMTAG; - else mmreg2 = EAX; - - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); - if( recMemConstRead32(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { - if( mmreg1&MEM_XMMTAG ) xmmregs[mmreg1&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); - written = 1; - } - ineax = recMemConstRead32(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_); - - if( !written ) { - if( !(mmreg1&MEM_XMMTAG) ) MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); - } - - if( ineax || !(mmreg2 & MEM_XMMTAG) ) { - if( mmreg2&MEM_XMMTAG ) xmmregs[mmreg2&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); - } - } - else -#endif - { - int dohw; - int regt, regtnext; - int mmregs = _eePrepareReg(_Rs_); - - _deleteMMXreg(MMX_FPU+_Rt_, 2); - regt = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); - regtnext = _allocCheckFPUtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE); - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); - - if( regt >= 0 ) { - SSEX_MOVD_RmOffset_to_XMM(regt, ECX, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); - } - - if( regtnext >= 0 ) { - SSEX_MOVD_RmOffset_to_XMM(regtnext, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - PUSH32R(ECX); - CALLFunc( (int)recMemRead32 ); - POP32R(ECX); - - if( regt >= 0 ) { - SSE2_MOVD_R_to_XMM(regt, EAX); - } - else { - MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); - } - - ADD32ItoR(ECX, _Imm_co_-_Imm_); - CALLFunc( (int)recMemRead32 ); - - if( regtnext >= 0 ) { - SSE2_MOVD_R_to_XMM(regtnext, EAX); - } - else { - MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); - } - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - } -} - -void recLWC1_coX(int num) -{ - int i; - int mmreg = -1; - int mmregs[XMMREGS]; - int nextrts[XMMREGS]; - - assert( num > 1 && num < XMMREGS ); - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - int ineax; - u32 written = 0; - mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; - else mmreg = EAX; - - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); - if( recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { - if( mmreg&MEM_XMMTAG ) xmmregs[mmreg&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); - written = 1; - } - else if( !IS_XMMREG(mmreg) ) MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); - - // recompile two at a time - for(i = 0; i < num-1; i += 2) { - nextrts[0] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - nextrts[1] = ((*(u32*)(PSM(pc+i*4+4)) >> 16) & 0x1F); - - written = 0; - mmregs[0] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrts[0], MODE_WRITE); - if( mmregs[0] >= 0 ) mmregs[0] |= MEM_XMMTAG; - else mmregs[0] = EBX; - - mmregs[1] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+2, nextrts[1], MODE_WRITE); - if( mmregs[1] >= 0 ) mmregs[1] |= MEM_XMMTAG; - else mmregs[1] = EAX; - - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); - if( recMemConstRead32(mmregs[0], g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4))) ) { - if( mmregs[0]&MEM_XMMTAG ) xmmregs[mmregs[0]&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[0] ].UL, EBX ); - written = 1; - } - ineax = recMemConstRead32(mmregs[1], g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4+4))); - - if( !written ) { - if( !(mmregs[0]&MEM_XMMTAG) ) MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[0] ].UL, EBX ); - } - - if( ineax || !(mmregs[1] & MEM_XMMTAG) ) { - if( mmregs[1]&MEM_XMMTAG ) xmmregs[mmregs[1]&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[1] ].UL, EAX ); - } - } - - if( i < num ) { - // one left - int nextrt = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - - mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrt, MODE_WRITE); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; - else mmreg = EAX; - - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); - if( recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4))) ) { - if( mmreg&MEM_XMMTAG ) xmmregs[mmreg&0xf].inuse = 0; - MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EBX ); - written = 1; - } - else if( !IS_XMMREG(mmreg) ) MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); - } - } - else -#endif - { - int dohw; - int mmregS = _eePrepareReg_coX(_Rs_, num); - - - _deleteMMXreg(MMX_FPU+_Rt_, 2); - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - _deleteMMXreg(MMX_FPU+nextrts[i], 2); - } - - mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); - - for(i = 0; i < num; ++i) { - mmregs[i] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE); - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 0, 1); - - if( mmreg >= 0 ) { - SSEX_MOVD_RmOffset_to_XMM(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); - } - - for(i = 0; i < num; ++i) { - if( mmregs[i] >= 0 ) { - SSEX_MOVD_RmOffset_to_XMM(mmregs[i], ECX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - else { - MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[i] ].UL, EAX ); - } - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - CALLFunc( (int)recMemRead32 ); - - if( mmreg >= 0 ) SSE2_MOVD_R_to_XMM(mmreg, EAX); - else MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); - - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); - CALLFunc( (int)recMemRead32 ); - - if( mmregs[i] >= 0 ) SSE2_MOVD_R_to_XMM(mmregs[i], EAX); - else MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[i] ].UL, EAX ); - } - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - } -} - -void recSWC1_co( void ) -{ - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - int mmreg; - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%4 == 0 ); - - mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(_Rt_<<16); - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - mmreg = EAX; - } - recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_, mmreg); - - mmreg = _checkXMMreg(XMMTYPE_FPREG, nextrt, MODE_READ); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(nextrt<<16); - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); - mmreg = EAX; - } - recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, mmreg); - } - else -#endif - { - int dohw; - int mmregs = _eePrepareReg(_Rs_); - - int mmreg1, mmreg2; - assert( _checkMMXreg(MMX_FPU+_Rt_, MODE_READ) == -1 ); - assert( _checkMMXreg(MMX_FPU+nextrt, MODE_READ) == -1 ); - - mmreg1 = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); - mmreg2 = _checkXMMreg(XMMTYPE_FPREG, nextrt, MODE_READ); - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); - - if(mmreg1 >= 0 ) { - if( mmreg2 >= 0 ) { - SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); - SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); - SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - } - else { - if( mmreg2 >= 0 ) { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - MOV32MtoR(EDX, (int)&fpuRegs.fpr[ nextrt ].UL); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - // some type of hardware write - if( mmreg1 >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg1); - else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - CALLFunc( (int)recMemWrite32 ); - - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, _Imm_co_-_Imm_); - if( mmreg2 >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg2); - else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); - CALLFunc( (int)recMemWrite32 ); - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - } -} - -void recSWC1_coX(int num) -{ - int i; - int mmreg = -1; - int mmregs[XMMREGS]; - int nextrts[XMMREGS]; - - assert( num > 1 && num < XMMREGS ); - - for(i = 0; i < num; ++i) { - nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); - } - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%4 == 0 ); - - mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(_Rt_<<16); - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - mmreg = EAX; - } - recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_, mmreg); - - for(i = 0; i < num; ++i) { - mmreg = _checkXMMreg(XMMTYPE_FPREG, nextrts[i], MODE_READ); - if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(nextrts[i]<<16); - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); - mmreg = EAX; - } - recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)), mmreg); - } - } - else -#endif - { - int dohw; - int mmregS = _eePrepareReg_coX(_Rs_, num); - - assert( _checkMMXreg(MMX_FPU+_Rt_, MODE_READ) == -1 ); - mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); - - for(i = 0; i < num; ++i) { - assert( _checkMMXreg(MMX_FPU+nextrts[i], MODE_READ) == -1 ); - mmregs[i] = _checkXMMreg(XMMTYPE_FPREG, nextrts[i], MODE_READ); - } - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 0, 1); - - if( mmreg >= 0) { - SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); - } - - for(i = 0; i < num; ++i) { - if( mmregs[i] >= 0) { - SSEX_MOVD_XMM_to_RmOffset(ECX, mmregs[i], PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - else { - MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); - } - } - - if( dohw ) { - if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); - else j8Ptr[2] = JMP8(0); - - SET_HWLOC_R5900(); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - // some type of hardware write - if( mmreg >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg); - else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); - CALLFunc( (int)recMemWrite32 ); - - for(i = 0; i < num; ++i) { - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); - if( mmregs[i] >= 0 && (xmmregs[mmregs[i]].mode&MODE_WRITE) ) SSE2_MOVD_XMM_to_R(EAX, mmregs[i]); - else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); - CALLFunc( (int)recMemWrite32 ); - } - - if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); - else x86SetJ8A(j8Ptr[2]); - } - } -} - -void recLQC2_co( void ) -{ - int mmreg1 = -1, mmreg2 = -1, t0reg = -1; - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - -#ifdef REC_SLOWREAD - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 16 == 0 ); - - if( _Ft_ ) mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); - else t0reg = _allocTempXMMreg(XMMT_FPS, -1); - recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg1 >= 0 ? mmreg1 : t0reg); - - if( nextrt ) mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_WRITE); - else if( t0reg < 0 ) t0reg = _allocTempXMMreg(XMMT_FPS, -1); - recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_)&~15, mmreg2 >= 0 ? mmreg2 : t0reg); - - if( t0reg >= 0 ) _freeXMMreg(t0reg); - } - else -#endif - { - u8* rawreadptr; - int dohw; - int mmregs = _eePrepareReg(_Rs_); - - if( _Ft_ ) mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); - if( nextrt ) mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_WRITE); - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); - - rawreadptr = x86Ptr[0]; - if( mmreg1 >= 0 ) SSEX_MOVDQARmtoROffset(mmreg1, ECX, PS2MEM_BASE_+s_nAddMemOffset); - if( mmreg2 >= 0 ) SSEX_MOVDQARmtoROffset(mmreg2, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - - if( dohw ) { - j8Ptr[1] = JMP8(0); - SET_HWLOC_R5900(); - - // check if writing to VUs - CMP32ItoR(ECX, 0x11000000); - JAE8(rawreadptr - (x86Ptr[0]+2)); - - MOV32RtoM((u32)&s_tempaddr, ECX); - - if( _Ft_ ) PUSH32I( (int)&VU0.VF[_Ft_].UD[0] ); - else PUSH32I( (int)&retValues[0] ); - CALLFunc( (int)recMemRead128 ); - - if( mmreg1 >= 0 ) SSEX_MOVDQA_M128_to_XMM(mmreg1, (int)&VU0.VF[_Ft_].UD[0] ); - - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, _Imm_co_-_Imm_); - - if( nextrt ) MOV32ItoRmOffset(ESP, (int)&VU0.VF[nextrt].UD[0], 0 ); - else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0 ); - CALLFunc( (int)recMemRead128 ); - - if( mmreg2 >= 0 ) SSEX_MOVDQA_M128_to_XMM(mmreg2, (int)&VU0.VF[nextrt].UD[0] ); - - ADD32ItoR(ESP, 4); - x86SetJ8(j8Ptr[1]); - } - } - - _clearNeededXMMregs(); // needed since allocing -} - -void recSQC2_co( void ) -{ - int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); - int mmreg1, mmreg2; - -#ifdef REC_SLOWWRITE - _flushConstReg(_Rs_); -#else - if( GPR_IS_CONST1( _Rs_ ) ) { - assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%16 == 0 ); - - mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_READ)|MEM_XMMTAG; - mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_READ)|MEM_XMMTAG; - recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg1); - recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_)&~15, mmreg2); - } - else -#endif - { - u8* rawreadptr; - int dohw; - int mmregs = _eePrepareReg(_Rs_); - - mmreg1 = _checkXMMreg(XMMTYPE_VFREG, _Ft_, MODE_READ); - mmreg2 = _checkXMMreg(XMMTYPE_VFREG, nextrt, MODE_READ); - - dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); - - rawreadptr = x86Ptr[0]; - - if( mmreg1 >= 0 ) { - SSEX_MOVDQARtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); - } - else { - if( _hasFreeXMMreg() ) { - mmreg1 = _allocTempXMMreg(XMMT_FPS, -1); - SSEX_MOVDQA_M128_to_XMM(mmreg1, (int)&VU0.VF[_Ft_].UD[0]); - SSEX_MOVDQARtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); - _freeXMMreg(mmreg1); - } - else if( _hasFreeMMXreg() ) { - mmreg1 = _allocMMXreg(-1, MMX_TEMP, 0); - MOVQMtoR(mmreg1, (int)&VU0.VF[_Ft_].UD[0]); - MOVQRtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); - MOVQMtoR(mmreg1, (int)&VU0.VF[_Ft_].UL[2]); - MOVQRtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset+8); - SetMMXstate(); - _freeMMXreg(mmreg1); - } - else { - MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[0]); - MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[1]); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); - MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+4); - MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[2]); - MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[3]); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+8); - MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+12); - } - } - - if( mmreg2 >= 0 ) { - SSEX_MOVDQARtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - } - else { - if( _hasFreeXMMreg() ) { - mmreg2 = _allocTempXMMreg(XMMT_FPS, -1); - SSEX_MOVDQA_M128_to_XMM(mmreg2, (int)&VU0.VF[nextrt].UD[0]); - SSEX_MOVDQARtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - _freeXMMreg(mmreg2); - } - else if( _hasFreeMMXreg() ) { - mmreg2 = _allocMMXreg(-1, MMX_TEMP, 0); - MOVQMtoR(mmreg2, (int)&VU0.VF[nextrt].UD[0]); - MOVQRtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOVQMtoR(mmreg2, (int)&VU0.VF[nextrt].UL[2]); - MOVQRtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); - SetMMXstate(); - _freeMMXreg(mmreg2); - } - else { - MOV32MtoR(EAX, (int)&VU0.VF[nextrt].UL[0]); - MOV32MtoR(EDX, (int)&VU0.VF[nextrt].UL[1]); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); - MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+4); - MOV32MtoR(EAX, (int)&VU0.VF[nextrt].UL[2]); - MOV32MtoR(EDX, (int)&VU0.VF[nextrt].UL[3]); - MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); - MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+12); - } - } - - if( dohw ) { - j8Ptr[1] = JMP8(0); - - SET_HWLOC_R5900(); - - // check if writing to VUs - CMP32ItoR(ECX, 0x11000000); - JAE8(rawreadptr - (x86Ptr[0]+2)); - - // some type of hardware write - if( mmreg1 >= 0) { - if( xmmregs[mmreg1].mode & MODE_WRITE ) { - SSEX_MOVDQA_XMM_to_M128((int)&VU0.VF[_Ft_].UD[0], mmreg1); - } - } - - MOV32RtoM((u32)&s_tempaddr, ECX); - - MOV32ItoR(EAX, (int)&VU0.VF[_Ft_].UD[0]); - CALLFunc( (int)recMemWrite128 ); - - if( mmreg2 >= 0) { - if( xmmregs[mmreg2].mode & MODE_WRITE ) { - SSEX_MOVDQA_XMM_to_M128((int)&VU0.VF[nextrt].UD[0], mmreg2); - } - } - - MOV32MtoR(ECX, (u32)&s_tempaddr); - ADD32ItoR(ECX, _Imm_co_-_Imm_); - - MOV32ItoR(EAX, (int)&VU0.VF[nextrt].UD[0]); - CALLFunc( (int)recMemWrite128 ); - - x86SetJ8A(j8Ptr[1]); - } - } -} - -#endif // PCSX2_VM_COISSUE