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) {
|
Update to v106r47 release.
byuu says:
This is probably the largest code-change diff I've done in years.
I spent four days working 10-16 hours a day reworking layouts in hiro
completely.
The result is we now have TableLayout, which will allow for better
horizontal+vertical combined alignment.
Windows, GTK2, and now GTK3 are fully supported.
Windows is getting the initial window geometry wrong by a bit.
GTK2 and GTK3 work perfectly. I basically abandoned trying to detect
resize signals, and instead keep a list of all hiro windows that are
allocated, and every time the main loop runs, it will query all of them
to see if they've been resized. I'm disgusted that I have to do this,
but after fighting with GTK for years, I'm about sick of it. GTK was
doing this crazy thing where it would trigger another size-allocate
inside of a previous size-allocate, and so my layouts would be halfway
through resizing all the widgets, and then the size-allocate would kick
off another one. That would end up leaving the rest of the first layout
loop with bad widget sizes. And if I detected a second re-entry and
blocked it, then the entire window would end up with the older geometry.
I started trying to build a message queue system to allow the second
layout resize to occur after the first one completed, but this was just
too much madness, so I went with the simpler solution.
Qt4 has some geometry problems, and doesn't show tab frame layouts
properly yet.
Qt5 causes an ICE error and tanks my entire Xorg display server, so ...
something is seriously wrong there, and it's not hiro's fault. Creating
a dummy Qt5 application without even using hiro, just int main() {
TestObject object; } with object performing a dynamic\_cast to a derived
type segfaults. Memory is getting corrupted where GCC allocates the
vtables for classes, just by linking in Qt. Could be somehow related to
the -fPIC requirement that only Qt5 has ... could just be that FreeBSD
10.1 has a buggy implementation of Qt5. I don't know. It's beyond my
ability to debug, so this one's going to stay broken.
The Cocoa port is busted. I'll fix it up to compile again, but that's
about all I'm going to do.
Many optimizations mean bsnes and higan open faster. GTK2 and GTK3 both
resize windows very quickly now.
higan crashes when you load a game, so that's not good. bsnes works
though.
bsnes also has the start of a localization engine now. Still a long way
to go.
The makefiles received a rather substantial restructuring. Including the
ruby and hiro makefiles will add the necessary compilation rules for
you, which also means that moc will run for the qt4 and qt5 targets, and
windres will run for the Windows targets.
2018-07-14 03:59:29 +00:00
|
|
|
for(auto n : reverse(value)) {
|
2016-10-27 21:16:58 +00:00
|
|
|
operator<<=(8);
|
Update to v106r47 release.
byuu says:
This is probably the largest code-change diff I've done in years.
I spent four days working 10-16 hours a day reworking layouts in hiro
completely.
The result is we now have TableLayout, which will allow for better
horizontal+vertical combined alignment.
Windows, GTK2, and now GTK3 are fully supported.
Windows is getting the initial window geometry wrong by a bit.
GTK2 and GTK3 work perfectly. I basically abandoned trying to detect
resize signals, and instead keep a list of all hiro windows that are
allocated, and every time the main loop runs, it will query all of them
to see if they've been resized. I'm disgusted that I have to do this,
but after fighting with GTK for years, I'm about sick of it. GTK was
doing this crazy thing where it would trigger another size-allocate
inside of a previous size-allocate, and so my layouts would be halfway
through resizing all the widgets, and then the size-allocate would kick
off another one. That would end up leaving the rest of the first layout
loop with bad widget sizes. And if I detected a second re-entry and
blocked it, then the entire window would end up with the older geometry.
I started trying to build a message queue system to allow the second
layout resize to occur after the first one completed, but this was just
too much madness, so I went with the simpler solution.
Qt4 has some geometry problems, and doesn't show tab frame layouts
properly yet.
Qt5 causes an ICE error and tanks my entire Xorg display server, so ...
something is seriously wrong there, and it's not hiro's fault. Creating
a dummy Qt5 application without even using hiro, just int main() {
TestObject object; } with object performing a dynamic\_cast to a derived
type segfaults. Memory is getting corrupted where GCC allocates the
vtables for classes, just by linking in Qt. Could be somehow related to
the -fPIC requirement that only Qt5 has ... could just be that FreeBSD
10.1 has a buggy implementation of Qt5. I don't know. It's beyond my
ability to debug, so this one's going to stay broken.
The Cocoa port is busted. I'll fix it up to compile again, but that's
about all I'm going to do.
Many optimizations mean bsnes and higan open faster. GTK2 and GTK3 both
resize windows very quickly now.
higan crashes when you load a game, so that's not good. bsnes works
though.
bsnes also has the start of a localization engine now. Still a long way
to go.
The makefiles received a rather substantial restructuring. Including the
ruby and hiro makefiles will add the necessary compilation rules for
you, which also means that moc will run for the qt4 and qt5 targets, and
windres will run for the Windows targets.
2018-07-14 03:59:29 +00:00
|
|
|
operator|=(n);
|
2016-10-27 21:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 v106r2 release.
byuu says:
Changelog:
- Super Famicom: added support for loading manifests without embedded
mapping information¹
- genius: initial commit
- various Makefile cleanups
¹: so the idea here is to try and aim for a stable manifest format,
and to allow direct transposition of icarus/genius database entries into
manifest files. The exact mechanics of how this is going to work is
currently in flux, but we'll get there.
For right now, `Super Famicom.sys` gains `boards.bml`, which is the raw
database from my board-editor tool, and higan itself tries to load
`boards.bml`, match an entry to game/board from the game's `manifest.bml`
file, and then transform it into the format currently used by higan. It
does this only when the game's `manifest.bml` file lacks a board node.
When such a board node exists, it works as previous versions of higan
did.
The only incompatible change right now is information/title is now
located at game/label. I may transition window title display to just use
the filenames instead.
Longer term, some thought is going to need to go into the format of the
`boards.bml` database itself, and at which point in the process I should
be transforming things.
Give it time, we'll refine this into something nicer.
2018-02-01 08:20:37 +00:00
|
|
|
#if 0
|
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);
|
|
|
|
}
|
Update to v106r2 release.
byuu says:
Changelog:
- Super Famicom: added support for loading manifests without embedded
mapping information¹
- genius: initial commit
- various Makefile cleanups
¹: so the idea here is to try and aim for a stable manifest format,
and to allow direct transposition of icarus/genius database entries into
manifest files. The exact mechanics of how this is going to work is
currently in flux, but we'll get there.
For right now, `Super Famicom.sys` gains `boards.bml`, which is the raw
database from my board-editor tool, and higan itself tries to load
`boards.bml`, match an entry to game/board from the game's `manifest.bml`
file, and then transform it into the format currently used by higan. It
does this only when the game's `manifest.bml` file lacks a board node.
When such a board node exists, it works as previous versions of higan
did.
The only incompatible change right now is information/title is now
located at game/label. I may transition window title display to just use
the filenames instead.
Longer term, some thought is going to need to go into the format of the
`boards.bml` database itself, and at which point in the process I should
be transforming things.
Give it time, we'll refine this into something nicer.
2018-02-01 08:20:37 +00:00
|
|
|
#endif
|
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
|
|
|
|
2016-10-27 21:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef ConcatenateType
|
|
|
|
#undef DeclareType
|
|
|
|
#undef Pair
|
|
|
|
#undef Type
|
|
|
|
#undef Half
|
|
|
|
#undef Cast
|