2016-10-27 21:16:58 +00:00
|
|
|
#define ConcatenateType(Size) uint##Size##_t
|
|
|
|
#define DeclareType(Size) ConcatenateType(Size)
|
|
|
|
|
|
|
|
#define Pair DeclareType(PairBits)
|
|
|
|
#define Type DeclareType(TypeBits)
|
|
|
|
#define Half DeclareType(HalfBits)
|
|
|
|
|
|
|
|
//pick the larger of two types to prevent unnecessary data clamping
|
|
|
|
#define Cast (typename conditional<sizeof(Pair) >= sizeof(T), Pair, T>::type)
|
|
|
|
|
|
|
|
namespace nall {
|
|
|
|
//namespace Arithmetic {
|
|
|
|
|
|
|
|
struct Pair {
|
|
|
|
Pair() = default;
|
|
|
|
explicit constexpr Pair(const Pair& source) : hi(source.hi), lo(source.lo) {}
|
|
|
|
template<typename Hi, typename Lo> constexpr Pair(const Hi& hi, const Lo& lo) : hi(hi), lo(lo) {}
|
|
|
|
template<typename T> Pair(const T& source) { _set(*this, source); }
|
|
|
|
|
|
|
|
explicit operator bool() const { return hi | lo; }
|
|
|
|
template<typename T> operator T() const { T value; _get(*this, value); return value; }
|
|
|
|
|
|
|
|
auto operator~() const -> Pair { return {~hi, ~lo}; }
|
|
|
|
auto operator!() const -> bool { return !(hi || lo); }
|
|
|
|
|
|
|
|
auto operator++() -> Pair& { lo++; hi += lo == 0; return *this; }
|
Update to v102r21 release.
byuu says:
Changelog:
- GBA: fixed WININ2 reads, BG3PB writes [Jonas Quinn]
- R65816: added support for yielding/resuming from WAI/STP¹
- SFC: removed status.dmaCounter functionality (also fixes possible
TAS desync issue)
- tomoko: added support for combinatorial inputs [hex\_usr\]²
- nall: fixed missing return value from Arithmetic::operator--
[Hendricks266]
Now would be the time to start looking for major regressions with the
new GBA PPU renderer, I suppose ...
¹: this doesn't matter for the master thread (SNES CPU), but is
important for slave threads (SNES SA1). If you try to save a state and
the SA1 is inside of a WAI instruction, it will get stuck there forever.
This was causing attempts to create a save state in Super Bomberman
- Panic Bomber W to deadlock the emulator and crash it. This is now
finally fixed.
Note that I still need to implement similar functionality into the Mega
Drive 68K and Z80 cores. They still have the possibility of deadlocking.
The SNES implementation was more a dry-run test for this new
functionality. This possible crashing bug in the Mega Drive core is the
major blocking bug for a new official release.
²: many, many thanks to hex\_usr for coming up with a really nice
design. I mostly implemented it the exact same way, but with a few tiny
differences that don't really matter (display " and ", " or " instead of
" & ", " | " in the input settings windows; append → bind;
assignmentName changed to displayName.)
The actual functionality is identical to the old higan v094 and earlier
builds. Emulated digital inputs let you combine multiple possible keys
to trigger the buttons. This is OR logic, so you can map to eg
keyboard.up OR gamepad.up for instance. Emulated analog inputs always
sum together. Emulated rumble outputs will cause all mapped devices to
rumble, which is probably not at all useful but whatever. Hotkeys use
AND logic, so you have to press every key mapped to trigger them. Useful
for eg Ctrl+F to trigger fullscreen.
Obviously, there are cases where OR logic would be nice for hotkeys,
too. Eg if you want both F11 and your gamepad's guide button to trigger
the fullscreen toggle. Unfortunately, this isn't supported, and likely
won't ever be in tomoko. Something I might consider is a throw switch in
the configuration file to swap between AND or OR logic for hotkeys, but
I'm not going to allow construction of mappings like "(Keyboard.Ctrl and
Keyboard.F) or Gamepad.Guide", as that's just too complicated to code,
and too complicated to make a nice GUI to set up the mappings for.
2017-06-06 13:44:40 +00:00
|
|
|
auto operator--() -> Pair& { hi -= lo == 0; lo--; return *this; }
|
2016-10-27 21:16:58 +00:00
|
|
|
|
|
|
|
auto operator++(int) -> Pair { Pair r = *this; lo++; hi += lo == 0; return r; }
|
|
|
|
auto operator--(int) -> Pair { Pair r = *this; hi -= lo == 0; lo--; return r; }
|
|
|
|
|
|
|
|
auto operator* (const Pair& rhs) const -> Pair { return mul(*this, rhs); }
|
|
|
|
auto operator/ (const Pair& rhs) const -> Pair { Pair q, r; div(*this, rhs, q, r); return q; }
|
|
|
|
auto operator% (const Pair& rhs) const -> Pair { Pair q, r; div(*this, rhs, q, r); return r; }
|
|
|
|
auto operator+ (const Pair& rhs) const -> Pair { return {hi + rhs.hi + (lo + rhs.lo < lo), lo + rhs.lo}; }
|
|
|
|
auto operator- (const Pair& rhs) const -> Pair { return {hi - rhs.hi - (lo - rhs.lo > lo), lo - rhs.lo}; }
|
|
|
|
auto operator<<(const Pair& rhs) const -> Pair { return shl(*this, rhs); }
|
|
|
|
auto operator>>(const Pair& rhs) const -> Pair { return shr(*this, rhs); }
|
|
|
|
auto operator& (const Pair& rhs) const -> Pair { return {hi & rhs.hi, lo & rhs.lo}; }
|
|
|
|
auto operator| (const Pair& rhs) const -> Pair { return {hi | rhs.hi, lo | rhs.lo}; }
|
|
|
|
auto operator^ (const Pair& rhs) const -> Pair { return {hi ^ rhs.hi, lo ^ rhs.lo}; }
|
|
|
|
auto operator==(const Pair& rhs) const -> bool { return hi == rhs.hi && lo == rhs.lo; }
|
|
|
|
auto operator!=(const Pair& rhs) const -> bool { return hi != rhs.hi || lo != rhs.lo; }
|
|
|
|
auto operator>=(const Pair& rhs) const -> bool { return hi > rhs.hi || (hi == rhs.hi && lo >= rhs.lo); }
|
|
|
|
auto operator<=(const Pair& rhs) const -> bool { return hi < rhs.hi || (hi == rhs.hi && lo <= rhs.lo); }
|
|
|
|
auto operator> (const Pair& rhs) const -> bool { return hi > rhs.hi || (hi == rhs.hi && lo > rhs.lo); }
|
|
|
|
auto operator< (const Pair& rhs) const -> bool { return hi < rhs.hi || (hi == rhs.hi && lo < rhs.lo); }
|
|
|
|
|
|
|
|
template<typename T> auto& operator*= (const T& rhs) { return *this = *this * Pair(rhs); }
|
|
|
|
template<typename T> auto& operator/= (const T& rhs) { return *this = *this / Pair(rhs); }
|
|
|
|
template<typename T> auto& operator%= (const T& rhs) { return *this = *this % Pair(rhs); }
|
|
|
|
template<typename T> auto& operator+= (const T& rhs) { return *this = *this + Pair(rhs); }
|
|
|
|
template<typename T> auto& operator-= (const T& rhs) { return *this = *this - Pair(rhs); }
|
|
|
|
template<typename T> auto& operator<<=(const T& rhs) { return *this = *this << Pair(rhs); }
|
|
|
|
template<typename T> auto& operator>>=(const T& rhs) { return *this = *this >> Pair(rhs); }
|
|
|
|
template<typename T> auto& operator&= (const T& rhs) { return *this = *this & Pair(rhs); }
|
|
|
|
template<typename T> auto& operator|= (const T& rhs) { return *this = *this | Pair(rhs); }
|
|
|
|
template<typename T> auto& operator^= (const T& rhs) { return *this = *this ^ Pair(rhs); }
|
|
|
|
|
|
|
|
template<typename T> auto operator* (const T& rhs) const { return Cast(*this) * Cast(rhs); }
|
|
|
|
template<typename T> auto operator/ (const T& rhs) const { return Cast(*this) / Cast(rhs); }
|
|
|
|
template<typename T> auto operator% (const T& rhs) const { return Cast(*this) % Cast(rhs); }
|
|
|
|
template<typename T> auto operator+ (const T& rhs) const { return Cast(*this) + Cast(rhs); }
|
|
|
|
template<typename T> auto operator- (const T& rhs) const { return Cast(*this) - Cast(rhs); }
|
|
|
|
template<typename T> auto operator<<(const T& rhs) const { return Cast(*this) << Cast(rhs); }
|
|
|
|
template<typename T> auto operator>>(const T& rhs) const { return Cast(*this) >> Cast(rhs); }
|
|
|
|
template<typename T> auto operator& (const T& rhs) const { return Cast(*this) & Cast(rhs); }
|
|
|
|
template<typename T> auto operator| (const T& rhs) const { return Cast(*this) | Cast(rhs); }
|
|
|
|
template<typename T> auto operator^ (const T& rhs) const { return Cast(*this) ^ Cast(rhs); }
|
|
|
|
|
|
|
|
template<typename T> auto operator==(const T& rhs) const -> bool { return Cast(*this) == Cast(rhs); }
|
|
|
|
template<typename T> auto operator!=(const T& rhs) const -> bool { return Cast(*this) != Cast(rhs); }
|
|
|
|
template<typename T> auto operator>=(const T& rhs) const -> bool { return Cast(*this) >= Cast(rhs); }
|
|
|
|
template<typename T> auto operator<=(const T& rhs) const -> bool { return Cast(*this) <= Cast(rhs); }
|
|
|
|
template<typename T> auto operator> (const T& rhs) const -> bool { return Cast(*this) > Cast(rhs); }
|
|
|
|
template<typename T> auto operator< (const T& rhs) const -> bool { return Cast(*this) < Cast(rhs); }
|
|
|
|
|
|
|
|
explicit Pair(const vector<uint8_t>& value) : hi(0), lo(0) {
|
|
|
|
for(auto n : rrange(value)) {
|
|
|
|
operator<<=(8);
|
|
|
|
operator|=(value[n]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Type lo;
|
|
|
|
Type hi;
|
|
|
|
|
|
|
|
friend auto upper(const Pair&) -> Type;
|
|
|
|
friend auto lower(const Pair&) -> Type;
|
|
|
|
friend auto bits(Pair) -> uint;
|
|
|
|
friend auto square(const Pair&) -> Pair;
|
|
|
|
friend auto square(const Pair&, Pair&, Pair&) -> void;
|
|
|
|
friend auto mul(const Pair&, const Pair&) -> Pair;
|
|
|
|
friend auto mul(const Pair&, const Pair&, Pair&, Pair&) -> void;
|
|
|
|
friend auto div(const Pair&, const Pair&, Pair&, Pair&) -> void;
|
|
|
|
template<typename T> friend auto shl(const Pair&, const T&) -> Pair;
|
|
|
|
template<typename T> friend auto shr(const Pair&, const T&) -> Pair;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define ConcatenateUDL(Size) _u##Size
|
|
|
|
#define DeclareUDL(Size) ConcatenateUDL(Size)
|
|
|
|
|
|
|
|
alwaysinline auto operator"" DeclareUDL(PairBits)(const char* s) -> Pair {
|
|
|
|
Pair p = 0;
|
|
|
|
if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
|
|
s += 2;
|
|
|
|
while(*s) {
|
|
|
|
auto c = *s++;
|
|
|
|
if(c == '\'');
|
|
|
|
else if(c >= '0' && c <= '9') p = (p << 4) + (c - '0');
|
|
|
|
else if(c >= 'a' && c <= 'f') p = (p << 4) + (c - 'a' + 10);
|
|
|
|
else if(c >= 'A' && c <= 'F') p = (p << 4) + (c - 'A' + 10);
|
|
|
|
else break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while(*s) {
|
|
|
|
auto c = *s++;
|
|
|
|
if(c == '\'');
|
|
|
|
else if(c >= '0' && c <= '9') p = (p << 3) + (p << 1) + (c - '0');
|
|
|
|
else break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef ConcatenateUDL
|
|
|
|
#undef DeclareUDL
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) == sizeof(T))> {
|
|
|
|
lhs = rhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) > sizeof(T))> {
|
|
|
|
lhs = {0, rhs};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) < sizeof(T))> {
|
|
|
|
lhs = {lower(rhs) >> TypeBits, lower(rhs)};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) == sizeof(Pair))> {
|
|
|
|
rhs = lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) > sizeof(Pair))> {
|
|
|
|
rhs = {0, lhs};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) < sizeof(Pair))> {
|
|
|
|
rhs = lower(lhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
alwaysinline auto upper(const Pair& value) -> Type { return value.hi; }
|
|
|
|
alwaysinline auto lower(const Pair& value) -> Type { return value.lo; }
|
|
|
|
|
|
|
|
alwaysinline auto bits(Pair value) -> uint {
|
|
|
|
if(value.hi) {
|
|
|
|
uint bits = TypeBits;
|
|
|
|
while(value.hi) value.hi >>= 1, bits++;
|
|
|
|
return bits;
|
|
|
|
} else {
|
|
|
|
uint bits = 0;
|
|
|
|
while(value.lo) value.lo >>= 1, bits++;
|
|
|
|
return bits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Bits * Bits => Bits
|
|
|
|
inline auto square(const Pair& lhs) -> Pair {
|
|
|
|
static const Type Mask = (Type(0) - 1) >> HalfBits;
|
|
|
|
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
|
|
|
|
Type dd = square(d), dc = d * c, db = d * b, da = d * a;
|
|
|
|
Type cc = square(c), cb = c * b;
|
|
|
|
|
|
|
|
Pair r0 = Pair(dd);
|
|
|
|
Pair r1 = Pair(dc) + Pair(dc) + Pair(r0 >> HalfBits);
|
|
|
|
Pair r2 = Pair(db) + Pair(cc) + Pair(db) + Pair(r1 >> HalfBits);
|
|
|
|
Pair r3 = Pair(da) + Pair(cb) + Pair(cb) + Pair(da) + Pair(r2 >> HalfBits);
|
|
|
|
|
|
|
|
return {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
|
|
|
|
}
|
|
|
|
|
|
|
|
//Bits * Bits => 2 * Bits
|
|
|
|
inline auto square(const Pair& lhs, Pair& hi, Pair& lo) -> void {
|
|
|
|
static const Type Mask = (Type(0) - 1) >> HalfBits;
|
|
|
|
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
|
|
|
|
Type dd = square(d), dc = d * c, db = d * b, da = d * a;
|
|
|
|
Type cc = square(c), cb = c * b, ca = c * a;
|
|
|
|
Type bb = square(b), ba = b * a;
|
|
|
|
Type aa = square(a);
|
|
|
|
|
|
|
|
Pair r0 = Pair(dd);
|
|
|
|
Pair r1 = Pair(dc) + Pair(dc) + Pair(r0 >> HalfBits);
|
|
|
|
Pair r2 = Pair(db) + Pair(cc) + Pair(db) + Pair(r1 >> HalfBits);
|
|
|
|
Pair r3 = Pair(da) + Pair(cb) + Pair(cb) + Pair(da) + Pair(r2 >> HalfBits);
|
|
|
|
Pair r4 = Pair(ca) + Pair(bb) + Pair(ca) + Pair(r3 >> HalfBits);
|
|
|
|
Pair r5 = Pair(ba) + Pair(ba) + Pair(r4 >> HalfBits);
|
|
|
|
Pair r6 = Pair(aa) + Pair(r5 >> HalfBits);
|
|
|
|
Pair r7 = Pair(r6 >> HalfBits);
|
|
|
|
|
|
|
|
hi = {(r7.lo & Mask) << HalfBits | (r6.lo & Mask), (r5.lo & Mask) << HalfBits | (r4.lo & Mask)};
|
|
|
|
lo = {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
|
|
|
|
}
|
|
|
|
|
|
|
|
//Bits * Bits => Bits
|
|
|
|
alwaysinline auto mul(const Pair& lhs, const Pair& rhs) -> Pair {
|
|
|
|
static const Type Mask = (Type(0) - 1) >> HalfBits;
|
|
|
|
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
|
|
|
|
Type e = rhs.hi >> HalfBits, f = rhs.hi & Mask, g = rhs.lo >> HalfBits, h = rhs.lo & Mask;
|
|
|
|
|
|
|
|
Pair r0 = Pair(d * h);
|
|
|
|
Pair r1 = Pair(c * h) + Pair(d * g) + Pair(r0 >> HalfBits);
|
|
|
|
Pair r2 = Pair(b * h) + Pair(c * g) + Pair(d * f) + Pair(r1 >> HalfBits);
|
|
|
|
Pair r3 = Pair(a * h) + Pair(b * g) + Pair(c * f) + Pair(d * e) + Pair(r2 >> HalfBits);
|
|
|
|
|
|
|
|
return {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
|
|
|
|
}
|
|
|
|
|
|
|
|
//Bits * Bits => 2 * Bits
|
|
|
|
alwaysinline auto mul(const Pair& lhs, const Pair& rhs, Pair& hi, Pair& lo) -> void {
|
|
|
|
static const Type Mask = (Type(0) - 1) >> HalfBits;
|
|
|
|
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
|
|
|
|
Type e = rhs.hi >> HalfBits, f = rhs.hi & Mask, g = rhs.lo >> HalfBits, h = rhs.lo & Mask;
|
|
|
|
|
|
|
|
Pair r0 = Pair(d * h);
|
|
|
|
Pair r1 = Pair(c * h) + Pair(d * g) + Pair(r0 >> HalfBits);
|
|
|
|
Pair r2 = Pair(b * h) + Pair(c * g) + Pair(d * f) + Pair(r1 >> HalfBits);
|
|
|
|
Pair r3 = Pair(a * h) + Pair(b * g) + Pair(c * f) + Pair(d * e) + Pair(r2 >> HalfBits);
|
|
|
|
Pair r4 = Pair(a * g) + Pair(b * f) + Pair(c * e) + Pair(r3 >> HalfBits);
|
|
|
|
Pair r5 = Pair(a * f) + Pair(b * e) + Pair(r4 >> HalfBits);
|
|
|
|
Pair r6 = Pair(a * e) + Pair(r5 >> HalfBits);
|
|
|
|
Pair r7 = Pair(r6 >> HalfBits);
|
|
|
|
|
|
|
|
hi = {(r7.lo & Mask) << HalfBits | (r6.lo & Mask), (r5.lo & Mask) << HalfBits | (r4.lo & Mask)};
|
|
|
|
lo = {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
|
|
|
|
}
|
|
|
|
|
|
|
|
alwaysinline auto div(const Pair& lhs, const Pair& rhs, Pair& quotient, Pair& remainder) -> void {
|
|
|
|
if(!rhs) throw std::runtime_error("division by zero");
|
|
|
|
quotient = 0, remainder = lhs;
|
|
|
|
if(!lhs || lhs < rhs) return;
|
|
|
|
|
|
|
|
auto count = bits(lhs) - bits(rhs);
|
|
|
|
Pair x = rhs << count;
|
|
|
|
Pair y = Pair(1) << count;
|
|
|
|
if(x > remainder) x >>= 1, y >>= 1;
|
|
|
|
while(remainder >= rhs) {
|
|
|
|
if(remainder >= x) remainder -= x, quotient |= y;
|
|
|
|
x >>= 1, y >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto shl(const Pair& lhs, const T& rhs) -> Pair {
|
|
|
|
if(!rhs) return lhs;
|
|
|
|
auto shift = (uint)rhs;
|
|
|
|
if(shift < TypeBits) {
|
|
|
|
return {lhs.hi << shift | lhs.lo >> (TypeBits - shift), lhs.lo << shift};
|
|
|
|
} else {
|
|
|
|
return {lhs.lo << (shift - TypeBits), 0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto shr(const Pair& lhs, const T& rhs) -> Pair {
|
|
|
|
if(!rhs) return lhs;
|
|
|
|
auto shift = (uint)rhs;
|
|
|
|
if(shift < TypeBits) {
|
|
|
|
return {lhs.hi >> shift, lhs.hi << (TypeBits - shift) | lhs.lo >> shift};
|
|
|
|
} else {
|
|
|
|
return {0, lhs.hi >> (shift - TypeBits)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto rol(const Pair& lhs, const T& rhs) -> Pair {
|
|
|
|
return lhs << rhs | lhs >> (PairBits - rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> alwaysinline auto ror(const Pair& lhs, const T& rhs) -> Pair {
|
|
|
|
return lhs >> rhs | lhs << (PairBits - rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EI enable_if_t<is_integral<T>::value>
|
|
|
|
|
|
|
|
template<typename T, EI> auto& operator*= (T& lhs, const Pair& rhs) { return lhs = lhs * T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator/= (T& lhs, const Pair& rhs) { return lhs = lhs / T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator%= (T& lhs, const Pair& rhs) { return lhs = lhs % T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator+= (T& lhs, const Pair& rhs) { return lhs = lhs + T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator-= (T& lhs, const Pair& rhs) { return lhs = lhs - T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator<<=(T& lhs, const Pair& rhs) { return lhs = lhs << T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator>>=(T& lhs, const Pair& rhs) { return lhs = lhs >> T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator&= (T& lhs, const Pair& rhs) { return lhs = lhs & T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator|= (T& lhs, const Pair& rhs) { return lhs = lhs | T(rhs); }
|
|
|
|
template<typename T, EI> auto& operator^= (T& lhs, const Pair& rhs) { return lhs = lhs ^ T(rhs); }
|
|
|
|
|
|
|
|
template<typename T, EI> auto operator* (const T& lhs, const Pair& rhs) { return Cast(lhs) * Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator/ (const T& lhs, const Pair& rhs) { return Cast(lhs) / Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator% (const T& lhs, const Pair& rhs) { return Cast(lhs) % Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator+ (const T& lhs, const Pair& rhs) { return Cast(lhs) + Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator- (const T& lhs, const Pair& rhs) { return Cast(lhs) - Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator<<(const T& lhs, const Pair& rhs) { return Cast(lhs) << Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator>>(const T& lhs, const Pair& rhs) { return Cast(lhs) >> Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator& (const T& lhs, const Pair& rhs) { return Cast(lhs) & Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator| (const T& lhs, const Pair& rhs) { return Cast(lhs) | Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator^ (const T& lhs, const Pair& rhs) { return Cast(lhs) ^ Cast(rhs); }
|
|
|
|
|
|
|
|
template<typename T, EI> auto operator==(const T& lhs, const Pair& rhs) { return Cast(lhs) == Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator!=(const T& lhs, const Pair& rhs) { return Cast(lhs) != Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator>=(const T& lhs, const Pair& rhs) { return Cast(lhs) >= Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator<=(const T& lhs, const Pair& rhs) { return Cast(lhs) <= Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator> (const T& lhs, const Pair& rhs) { return Cast(lhs) > Cast(rhs); }
|
|
|
|
template<typename T, EI> auto operator< (const T& lhs, const Pair& rhs) { return Cast(lhs) < Cast(rhs); }
|
|
|
|
|
|
|
|
#undef EI
|
|
|
|
|
|
|
|
template<> struct stringify<Pair> {
|
|
|
|
stringify(Pair source) {
|
|
|
|
char _output[1 + sizeof(Pair) * 3];
|
|
|
|
auto p = (char*)&_output;
|
|
|
|
do {
|
|
|
|
Pair quotient, remainder;
|
|
|
|
div(source, 10, quotient, remainder);
|
2017-01-22 00:33:36 +00:00
|
|
|
*p++ = remainder + '0';
|
2016-10-27 21:16:58 +00:00
|
|
|
source = quotient;
|
|
|
|
} while(source);
|
|
|
|
_size = p - _output;
|
|
|
|
*p = 0;
|
|
|
|
for(int x = _size - 1, y = 0; x >= 0 && y < _size; x--, y++) _data[x] = _output[y];
|
|
|
|
}
|
|
|
|
|
|
|
|
auto data() const -> const char* { return _data; }
|
|
|
|
auto size() const -> uint { return _size; }
|
|
|
|
char _data[1 + sizeof(Pair) * 3];
|
|
|
|
uint _size;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline auto to_vector(Pair value) -> vector<uint8_t> {
|
|
|
|
vector<uint8_t> result;
|
|
|
|
result.resize(PairBits / 8);
|
|
|
|
for(auto& byte : result) {
|
|
|
|
byte = value;
|
|
|
|
value >>= 8;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
Update to v102r12 release.
byuu says:
Changelog:
- MD/PSG: fixed 68K bus Z80 status read address location
- MS, GG, MD/PSG: channels post-decrement their counters, not
pre-decrement [Cydrak]¹
- MD/VDP: cache screen width registers once per scanline; screen
height registers once per frame
- MD/VDP: support 256-width display mode (used in Shining Force, etc)
- MD/YM2612: implemented timers²
- MD/YM2612: implemented 8-bit PCM DAC²
- 68000: TRAP instruction should index the vector location by 32 (eg
by 128 bytes), fixes Shining Force
- nall: updated hex(), octal(), binary() functions to take uintmax
instead of template<typename T> parameter³
¹: this one makes an incredible difference. Sie noticed that lots of
games set a period of 0, which would end up being a really long period
with pre-decrement. By fixing this, noise shows up in many more games,
and sounds way better in games even where it did before. You can hear
extra sound on Lunar - Sanposuru Gakuen's title screen, the noise in
Sonic The Hedgehog (Mega Drive) sounds better, etc.
²: this also really helps sound. The timers allow PSG music to play
back at the correct speed instead of playing back way too quickly. And
the PCM DAC lets you hear a lot of drum effects, as well as the
"Sega!!" sound at the start of Sonic the Hedgehog, and the infamous,
"Rise from your grave!" line from Altered Beast.
Still, most music on the Mega Drive comes from the FM channels, so
there's still not a whole lot to listen to.
I didn't implement Cydrak's $02c test register just yet. Sie wasn't 100%
certain on how the extended DAC bit worked, so I'd like to play it a
little conservative and get sound working, then I'll go back and add a
toggle or something to enable undocumented registers, that way we can
use that to detect any potential problems they might be causing.
³: unfortunately we lose support for using hex() on nall/arithmetic
types. If I have a const Pair& version of the function, then the
compiler gets confused on whether Natural<32> should use uintmax or
const Pair&, because compilers are stupid, and you can't have explicit
arguments in overloaded functions. So even though either function would
work, it just decides to error out instead >_>
This is actually really annoying, because I want hex() to be useful for
printing out nall/crypto keys and hashes directly.
But ... this change had to be made. Negative signed integers would crash
programs, and that was taking out my 68000 disassembler.
2017-02-27 08:45:51 +00:00
|
|
|
/*
|
|
|
|
inline auto hex(const Pair& value, long precision = 0, char padchar = '0') -> string {
|
|
|
|
string text;
|
|
|
|
if(!upper(value)) {
|
|
|
|
text.append(hex(lower(value)));
|
|
|
|
} else {
|
|
|
|
text.append(hex(upper(value)));
|
|
|
|
text.append(hex(lower(value), TypeBits / 4, '0'));
|
|
|
|
}
|
|
|
|
return pad(text, precision, padchar);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2016-10-27 21:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef ConcatenateType
|
|
|
|
#undef DeclareType
|
|
|
|
#undef Pair
|
|
|
|
#undef Type
|
|
|
|
#undef Half
|
|
|
|
#undef Cast
|