mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-23 17:09:44 +00:00
Update to v094r06 release.
byuu says: New terminal is in. Much nicer to use now. Command history makes a major difference in usability. The SMP is now fully traceable and debuggable. Basically they act as separate entities, you can trace both at the same time, but for the most part running and stepping is performed on the chip you select. I'm going to put off CPU+SMP interleave support for a while. I don't actually think it'll be too hard. Will get trickier if/when we support coprocessor debugging. Remaining tasks: - aliases - hotkeys - save states - window geometry Basically, the debugger's done. Just have to add the UI fluff. I also removed tracing/memory export from higan. It was always meant to be temporary until the debugger was remade.
This commit is contained in:
parent
423a6c6bf8
commit
3016e595f0
4
Makefile
4
Makefile
@ -6,8 +6,8 @@ gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := higan
|
||||
# target := loki
|
||||
# target := higan
|
||||
target := loki
|
||||
|
||||
ifeq ($(target),loki)
|
||||
options += debugger
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "094.05";
|
||||
static const char Version[] = "094.06";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
@ -111,10 +111,6 @@ struct Interface {
|
||||
//utility functions
|
||||
enum class PaletteMode : unsigned { Literal, Channel, Standard, Emulation };
|
||||
virtual void paletteUpdate(PaletteMode mode) {}
|
||||
|
||||
//debugger functions
|
||||
virtual bool tracerEnable(bool) { return false; }
|
||||
virtual void exportMemory() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,13 +16,13 @@ void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return {true, code.data};
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ struct Cheat {
|
||||
void reset();
|
||||
void append(unsigned addr, unsigned data);
|
||||
void append(unsigned addr, unsigned comp, unsigned data);
|
||||
optional<unsigned> find(unsigned addr, unsigned comp);
|
||||
maybe<unsigned> find(unsigned addr, unsigned comp);
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
|
@ -16,13 +16,13 @@ void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return {true, code.data};
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ struct Cheat {
|
||||
void reset();
|
||||
void append(unsigned addr, unsigned data);
|
||||
void append(unsigned addr, unsigned comp, unsigned data);
|
||||
optional<unsigned> find(unsigned addr, unsigned comp);
|
||||
maybe<unsigned> find(unsigned addr, unsigned comp);
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
|
@ -56,21 +56,20 @@ void Player::frame() {
|
||||
}
|
||||
}
|
||||
|
||||
optional<uint16> Player::keyinput() {
|
||||
if(status.logoDetected == false) return false;
|
||||
|
||||
switch(status.logoCounter) {
|
||||
case 0: return {true, 0x03ff};
|
||||
case 1: return {true, 0x03ff};
|
||||
case 2: return {true, 0x030f};
|
||||
maybe<uint16> Player::keyinput() {
|
||||
if(status.logoDetected) {
|
||||
switch(status.logoCounter) {
|
||||
case 0: return 0x03ff;
|
||||
case 1: return 0x03ff;
|
||||
case 2: return 0x030f;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
optional<uint32> Player::read() {
|
||||
if(status.enable == false) return false;
|
||||
|
||||
return {true, status.send};
|
||||
maybe<uint32> Player::read() {
|
||||
if(status.enable) return status.send;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
void Player::write(uint8 byte, uint2 addr) {
|
||||
|
@ -14,8 +14,8 @@ struct Player {
|
||||
void power();
|
||||
void frame();
|
||||
|
||||
optional<uint16> keyinput();
|
||||
optional<uint32> read();
|
||||
maybe<uint16> keyinput();
|
||||
maybe<uint32> read();
|
||||
void write(uint8 byte, uint2 addr);
|
||||
|
||||
void serialize(serializer& s);
|
||||
|
@ -88,19 +88,19 @@ public:
|
||||
length = size;
|
||||
}
|
||||
|
||||
optional<T&> find(const T& value) {
|
||||
if(!pool) return false;
|
||||
maybe<T&> find(const T& value) {
|
||||
if(!pool) return nothing;
|
||||
|
||||
unsigned hash = value.hash() & (length - 1);
|
||||
while(pool[hash]) {
|
||||
if(value == *pool[hash]) return {true, *pool[hash]};
|
||||
if(value == *pool[hash]) return *pool[hash];
|
||||
if(++hash >= length) hash = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
optional<T&> insert(const T& value) {
|
||||
maybe<T&> insert(const T& value) {
|
||||
if(!pool) pool = new T*[length]();
|
||||
|
||||
//double pool size when load is >= 50%
|
||||
@ -111,7 +111,7 @@ public:
|
||||
while(pool[hash]) if(++hash >= length) hash = 0;
|
||||
pool[hash] = new T(value);
|
||||
|
||||
return {true, *pool[hash]};
|
||||
return *pool[hash];
|
||||
}
|
||||
|
||||
bool remove(const T& value) {
|
||||
|
12
nall/hid.hpp
12
nall/hid.hpp
@ -23,11 +23,11 @@ namespace HID {
|
||||
input.append({name});
|
||||
}
|
||||
|
||||
optional<unsigned> find(const string& name) {
|
||||
maybe<unsigned> find(const string& name) {
|
||||
for(unsigned id = 0; id < input.size(); id++) {
|
||||
if(input[id].name == name) return {true, id};
|
||||
if(input[id].name == name) return id;
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
};
|
||||
|
||||
@ -50,11 +50,11 @@ namespace HID {
|
||||
group.append({name});
|
||||
}
|
||||
|
||||
optional<unsigned> find(const string& name) {
|
||||
maybe<unsigned> find(const string& name) {
|
||||
for(unsigned id = 0; id < group.size(); id++) {
|
||||
if(group[id].name == name) return {true, id};
|
||||
if(group[id].name == name) return id;
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
};
|
||||
|
||||
|
100
nall/ips.hpp
100
nall/ips.hpp
@ -1,100 +0,0 @@
|
||||
#ifndef NALL_IPS_HPP
|
||||
#define NALL_IPS_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct ips {
|
||||
inline bool apply();
|
||||
inline void source(const uint8_t* data, unsigned size);
|
||||
inline void modify(const uint8_t* data, unsigned size);
|
||||
inline ips();
|
||||
inline ~ips();
|
||||
|
||||
uint8_t* data = nullptr;
|
||||
unsigned size = 0;
|
||||
const uint8_t* sourceData = nullptr;
|
||||
unsigned sourceSize = 0;
|
||||
const uint8_t* modifyData = nullptr;
|
||||
unsigned modifySize = 0;
|
||||
};
|
||||
|
||||
bool ips::apply() {
|
||||
if(modifySize < 8) return false;
|
||||
if(modifyData[0] != 'P') return false;
|
||||
if(modifyData[1] != 'A') return false;
|
||||
if(modifyData[2] != 'T') return false;
|
||||
if(modifyData[3] != 'C') return false;
|
||||
if(modifyData[4] != 'H') return false;
|
||||
|
||||
if(data) delete[] data;
|
||||
data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
|
||||
size = sourceSize;
|
||||
memcpy(data, sourceData, sourceSize);
|
||||
unsigned offset = 5;
|
||||
|
||||
while(true) {
|
||||
unsigned address, length;
|
||||
|
||||
if(offset > modifySize - 3) break;
|
||||
address = modifyData[offset++] << 16;
|
||||
address |= modifyData[offset++] << 8;
|
||||
address |= modifyData[offset++] << 0;
|
||||
|
||||
if(address == 0x454f46) { //EOF
|
||||
if(offset == modifySize) return true;
|
||||
if(offset == modifySize - 3) {
|
||||
size = modifyData[offset++] << 16;
|
||||
size |= modifyData[offset++] << 8;
|
||||
size |= modifyData[offset++] << 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(offset > modifySize - 2) break;
|
||||
length = modifyData[offset++] << 8;
|
||||
length |= modifyData[offset++] << 0;
|
||||
|
||||
if(length) { //Copy
|
||||
if(offset > modifySize - length) break;
|
||||
while(length--) data[address++] = modifyData[offset++];
|
||||
} else { //RLE
|
||||
if(offset > modifySize - 3) break;
|
||||
length = modifyData[offset++] << 8;
|
||||
length |= modifyData[offset++] << 0;
|
||||
if(length == 0) break; //illegal
|
||||
while(length--) data[address++] = modifyData[offset];
|
||||
offset++;
|
||||
}
|
||||
|
||||
size = max(size, address);
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ips::source(const uint8_t* data, unsigned size) {
|
||||
sourceData = data, sourceSize = size;
|
||||
}
|
||||
|
||||
void ips::modify(const uint8_t* data, unsigned size) {
|
||||
modifyData = data, modifySize = size;
|
||||
}
|
||||
|
||||
ips::ips() {
|
||||
}
|
||||
|
||||
ips::~ips() {
|
||||
if(data) delete[] data;
|
||||
if(sourceData) delete[] sourceData;
|
||||
if(modifyData) delete[] modifyData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
10
nall/map.hpp
10
nall/map.hpp
@ -16,9 +16,9 @@ template<typename T, typename U> struct map {
|
||||
node_t(const T& key, const U& value) : key(key), value(value) {}
|
||||
};
|
||||
|
||||
optional<U> find(const T& key) const {
|
||||
if(auto node = root.find({key})) return {true, node().value};
|
||||
return false;
|
||||
maybe<U&> find(const T& key) const {
|
||||
if(auto node = root.find({key})) return node().value;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
void insert(const T& key, const U& value) { root.insert({key, value}); }
|
||||
@ -36,8 +36,8 @@ protected:
|
||||
};
|
||||
|
||||
template<typename T, typename U> struct bimap {
|
||||
optional<U> find(const T& key) const { return tmap.find(key); }
|
||||
optional<T> find(const U& key) const { return umap.find(key); }
|
||||
maybe<U&> find(const T& key) const { return tmap.find(key); }
|
||||
maybe<T&> find(const U& key) const { return umap.find(key); }
|
||||
void insert(const T& key, const U& value) { tmap.insert(key, value); umap.insert(value, key); }
|
||||
void remove(const T& key) { if(auto p = tmap.find(key)) { umap.remove(p().value); tmap.remove(key); } }
|
||||
void remove(const U& key) { if(auto p = umap.find(key)) { tmap.remove(p().value); umap.remove(key); } }
|
||||
|
76
nall/maybe.hpp
Normal file
76
nall/maybe.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef NALL_MAYBE_HPP
|
||||
#define NALL_MAYBE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct nothing_t {};
|
||||
static nothing_t nothing;
|
||||
|
||||
template<typename T>
|
||||
class maybe {
|
||||
T* value = nullptr;
|
||||
|
||||
public:
|
||||
maybe() {}
|
||||
maybe(nothing_t) {}
|
||||
maybe(const T& source) { operator=(source); }
|
||||
maybe(const maybe& source) { operator=(source); }
|
||||
maybe(maybe&& source) { operator=(std::move(source)); }
|
||||
~maybe() { reset(); }
|
||||
|
||||
maybe& operator=(nothing_t) { reset(); return *this; }
|
||||
maybe& operator=(const T& source) { reset(); value = new T(source); return *this; }
|
||||
maybe& operator=(const maybe& source) { reset(); if(source) value = new T(source()); return *this; }
|
||||
maybe& operator=(maybe&& source) { reset(); value = source.value; source.value = nullptr; return *this; }
|
||||
|
||||
bool operator==(const maybe& source) const {
|
||||
if(value && source.value) return *value == *source.value;
|
||||
return !value && !source.value;
|
||||
}
|
||||
bool operator!=(const maybe& source) const { return !operator==(source); }
|
||||
|
||||
explicit operator bool() const { return value; }
|
||||
T& operator()() { assert(value); return *value; }
|
||||
const T& operator()() const { assert(value); return *value; }
|
||||
const T& operator()(const T& invalid) const { if(value) return *value; return invalid; }
|
||||
|
||||
bool empty() const { return value == nullptr; }
|
||||
void reset() { if(value) { delete value; value = nullptr; } }
|
||||
void swap(maybe& source) { std::swap(value, source.value); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class maybe<T&> {
|
||||
T* value = nullptr;
|
||||
|
||||
public:
|
||||
maybe() {}
|
||||
maybe(nothing_t) {}
|
||||
maybe(const T& source) { operator=(source); }
|
||||
maybe(const maybe& source) { operator=(source); }
|
||||
maybe(maybe&& source) { operator=(std::move(source)); }
|
||||
|
||||
maybe& operator=(nothing_t) { value = nullptr; return *this; }
|
||||
maybe& operator=(const T& source) { value = (T*)&source; return *this; }
|
||||
maybe& operator=(const maybe& source) { value = source.value; return *this; }
|
||||
maybe& operator=(maybe&& source) { value = source.value; source.value = nullptr; return *this; }
|
||||
|
||||
bool operator==(const maybe& source) const {
|
||||
if(value && source.value) return *value == *source.value;
|
||||
return !value && !source.value;
|
||||
}
|
||||
bool operator!=(const maybe& source) const { return !operator==(source); }
|
||||
|
||||
explicit operator bool() const { return value; }
|
||||
T& operator()() { assert(value); return *value; }
|
||||
const T& operator()() const { assert(value); return *value; }
|
||||
const T& operator()(const T& invalid) const { if(value) return *value; return invalid; }
|
||||
|
||||
bool empty() const { return value == nullptr; }
|
||||
void reset() { value = nullptr; }
|
||||
void swap(maybe& source) { std::swap(value, source.value); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -34,6 +34,7 @@
|
||||
#include <nall/invoke.hpp>
|
||||
#include <nall/map.hpp>
|
||||
#include <nall/matrix.hpp>
|
||||
#include <nall/maybe.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/random.hpp>
|
||||
|
14
nall/set.hpp
14
nall/set.hpp
@ -45,22 +45,22 @@ template<typename T> struct set {
|
||||
nodes = 0;
|
||||
}
|
||||
|
||||
optional<T&> find(const T& value) {
|
||||
maybe<T&> find(const T& value) {
|
||||
if(node_t* node = find(root, value)) return node->value;
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
optional<const T&> find(const T& value) const {
|
||||
maybe<const T&> find(const T& value) const {
|
||||
if(node_t* node = find(root, value)) return node->value;
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
optional<T&> insert(const T& value) {
|
||||
maybe<T&> insert(const T& value) {
|
||||
unsigned count = size();
|
||||
node_t* v = insert(root, value);
|
||||
root->red = 0;
|
||||
if(size() == count) return false;
|
||||
return {true, v->value};
|
||||
if(size() == count) return nothing;
|
||||
return v->value;
|
||||
}
|
||||
|
||||
template<typename... Args> bool insert(const T& value, Args&&... args) {
|
||||
|
@ -107,10 +107,10 @@ public:
|
||||
|
||||
inline string& strip();
|
||||
|
||||
inline optional<unsigned> find(rstring key) const;
|
||||
inline optional<unsigned> ifind(rstring key) const;
|
||||
inline optional<unsigned> qfind(rstring key) const;
|
||||
inline optional<unsigned> iqfind(rstring key) const;
|
||||
inline maybe<unsigned> find(rstring key) const;
|
||||
inline maybe<unsigned> ifind(rstring key) const;
|
||||
inline maybe<unsigned> qfind(rstring key) const;
|
||||
inline maybe<unsigned> iqfind(rstring key) const;
|
||||
|
||||
//core.hpp
|
||||
inline explicit operator bool() const;
|
||||
@ -155,7 +155,7 @@ public:
|
||||
|
||||
//list.hpp
|
||||
struct lstring : vector<string> {
|
||||
inline optional<unsigned> find(rstring) const;
|
||||
inline maybe<unsigned> find(rstring) const;
|
||||
inline string merge(const string&) const;
|
||||
inline lstring& isort();
|
||||
inline lstring& strip();
|
||||
|
@ -36,11 +36,11 @@ inline bool strccat(char* target, const char* source, unsigned length);
|
||||
inline void strpcpy(char*& target, const char* source, unsigned& length);
|
||||
|
||||
//strpos.hpp
|
||||
inline optional<unsigned> strpos(const char* str, const char* key);
|
||||
inline optional<unsigned> istrpos(const char* str, const char* key);
|
||||
inline optional<unsigned> qstrpos(const char* str, const char* key);
|
||||
inline optional<unsigned> iqstrpos(const char* str, const char* key);
|
||||
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(const char* str, const char* key);
|
||||
inline maybe<unsigned> strpos(const char* str, const char* key);
|
||||
inline maybe<unsigned> istrpos(const char* str, const char* key);
|
||||
inline maybe<unsigned> qstrpos(const char* str, const char* key);
|
||||
inline maybe<unsigned> iqstrpos(const char* str, const char* key);
|
||||
template<bool Insensitive = false, bool Quoted = false> inline maybe<unsigned> ustrpos(const char* str, const char* key);
|
||||
|
||||
//trim.hpp
|
||||
template<unsigned Limit = 0> inline char* ltrim(char* str, const char* key = " ");
|
||||
|
@ -7,26 +7,26 @@
|
||||
namespace nall {
|
||||
|
||||
template<bool Insensitive, bool Quoted>
|
||||
optional<unsigned> ustrpos(const char* str, const char* key) {
|
||||
maybe<unsigned> ustrpos(const char* str, const char* key) {
|
||||
const char* base = str;
|
||||
|
||||
while(*str) {
|
||||
if(quoteskip<Quoted>(str)) continue;
|
||||
for(unsigned n = 0;; n++) {
|
||||
if(key[n] == 0) return {true, (unsigned)(str - base)};
|
||||
if(str[n] == 0) return false;
|
||||
if(key[n] == 0) return (unsigned)(str - base);
|
||||
if(str[n] == 0) return nothing;
|
||||
if(!chrequal<Insensitive>(str[n], key[n])) break;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
optional<unsigned> strpos(const char* str, const char* key) { return ustrpos<false, false>(str, key); }
|
||||
optional<unsigned> istrpos(const char* str, const char* key) { return ustrpos<true, false>(str, key); }
|
||||
optional<unsigned> qstrpos(const char* str, const char* key) { return ustrpos<false, true>(str, key); }
|
||||
optional<unsigned> iqstrpos(const char* str, const char* key) { return ustrpos<true, true>(str, key); }
|
||||
maybe<unsigned> strpos(const char* str, const char* key) { return ustrpos<false, false>(str, key); }
|
||||
maybe<unsigned> istrpos(const char* str, const char* key) { return ustrpos<true, false>(str, key); }
|
||||
maybe<unsigned> qstrpos(const char* str, const char* key) { return ustrpos<false, true>(str, key); }
|
||||
maybe<unsigned> iqstrpos(const char* str, const char* key) { return ustrpos<true, true>(str, key); }
|
||||
|
||||
}
|
||||
|
||||
|
@ -93,16 +93,16 @@ inline int64_t evaluateInteger(Node* node) {
|
||||
throw "invalid operator";
|
||||
}
|
||||
|
||||
inline optional<int64_t> integer(const string& expression) {
|
||||
inline maybe<int64_t> integer(const string& expression) {
|
||||
try {
|
||||
auto tree = new Node;
|
||||
const char* p = expression;
|
||||
parse(tree, p, 0);
|
||||
auto result = evaluateInteger(tree);
|
||||
delete tree;
|
||||
return {true, result};
|
||||
return result;
|
||||
} catch(const char*) {
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,16 +138,16 @@ inline long double evaluateReal(Node* node) {
|
||||
throw "invalid operator";
|
||||
}
|
||||
|
||||
inline optional<long double> real(const string& expression) {
|
||||
inline maybe<long double> real(const string& expression) {
|
||||
try {
|
||||
auto tree = new Node;
|
||||
const char* p = expression;
|
||||
parse(tree, p, 0);
|
||||
auto result = evaluateReal(tree);
|
||||
delete tree;
|
||||
return {true, result};
|
||||
return result;
|
||||
} catch(const char*) {
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
optional<unsigned> lstring::find(rstring key) const {
|
||||
maybe<unsigned> lstring::find(rstring key) const {
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
if(operator[](i) == key) return {true, i};
|
||||
if(operator[](i) == key) return i;
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
string lstring::merge(const string& separator) const {
|
||||
|
@ -114,10 +114,10 @@ string& string::strip() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional<unsigned> string::find(rstring key) const { return strpos(data(), key); }
|
||||
optional<unsigned> string::ifind(rstring key) const { return istrpos(data(), key); }
|
||||
optional<unsigned> string::qfind(rstring key) const { return qstrpos(data(), key); }
|
||||
optional<unsigned> string::iqfind(rstring key) const { return iqstrpos(data(), key); }
|
||||
maybe<unsigned> string::find(rstring key) const { return strpos(data(), key); }
|
||||
maybe<unsigned> string::ifind(rstring key) const { return istrpos(data(), key); }
|
||||
maybe<unsigned> string::qfind(rstring key) const { return qstrpos(data(), key); }
|
||||
maybe<unsigned> string::iqfind(rstring key) const { return iqstrpos(data(), key); }
|
||||
|
||||
}
|
||||
|
||||
|
225
nall/ups.hpp
225
nall/ups.hpp
@ -1,225 +0,0 @@
|
||||
#ifndef NALL_UPS_HPP
|
||||
#define NALL_UPS_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct ups {
|
||||
enum class result : unsigned {
|
||||
unknown,
|
||||
success,
|
||||
patch_unwritable,
|
||||
patch_invalid,
|
||||
source_invalid,
|
||||
target_invalid,
|
||||
target_too_small,
|
||||
patch_checksum_invalid,
|
||||
source_checksum_invalid,
|
||||
target_checksum_invalid,
|
||||
};
|
||||
|
||||
function<void (unsigned offset, unsigned length)> progress;
|
||||
|
||||
result create(
|
||||
const uint8_t* sourcedata, unsigned sourcelength,
|
||||
const uint8_t* targetdata, unsigned targetlength,
|
||||
const char* patchfilename
|
||||
) {
|
||||
source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata;
|
||||
source_length = sourcelength, target_length = targetlength;
|
||||
source_offset = target_offset = 0;
|
||||
source_checksum = target_checksum = patch_checksum = ~0;
|
||||
|
||||
if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable;
|
||||
|
||||
patch_write('U');
|
||||
patch_write('P');
|
||||
patch_write('S');
|
||||
patch_write('1');
|
||||
encode(source_length);
|
||||
encode(target_length);
|
||||
|
||||
unsigned output_length = source_length > target_length ? source_length : target_length;
|
||||
unsigned relative = 0;
|
||||
for(unsigned offset = 0; offset < output_length;) {
|
||||
uint8_t x = source_read();
|
||||
uint8_t y = target_read();
|
||||
|
||||
if(x == y) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
encode(offset++ - relative);
|
||||
patch_write(x ^ y);
|
||||
|
||||
while(true) {
|
||||
if(offset >= output_length) {
|
||||
patch_write(0x00);
|
||||
break;
|
||||
}
|
||||
|
||||
x = source_read();
|
||||
y = target_read();
|
||||
offset++;
|
||||
patch_write(x ^ y);
|
||||
if(x == y) break;
|
||||
}
|
||||
|
||||
relative = offset;
|
||||
}
|
||||
|
||||
source_checksum = ~source_checksum;
|
||||
target_checksum = ~target_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8));
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8));
|
||||
uint32_t patch_result_checksum = ~patch_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8));
|
||||
|
||||
patch_file.close();
|
||||
return result::success;
|
||||
}
|
||||
|
||||
result apply(
|
||||
const uint8_t* patchdata, unsigned patchlength,
|
||||
const uint8_t* sourcedata, unsigned sourcelength,
|
||||
uint8_t* targetdata, unsigned& targetlength
|
||||
) {
|
||||
patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata;
|
||||
patch_length = patchlength, source_length = sourcelength, target_length = targetlength;
|
||||
patch_offset = source_offset = target_offset = 0;
|
||||
patch_checksum = source_checksum = target_checksum = ~0;
|
||||
|
||||
if(patch_length < 18) return result::patch_invalid;
|
||||
if(patch_read() != 'U') return result::patch_invalid;
|
||||
if(patch_read() != 'P') return result::patch_invalid;
|
||||
if(patch_read() != 'S') return result::patch_invalid;
|
||||
if(patch_read() != '1') return result::patch_invalid;
|
||||
|
||||
unsigned source_read_length = decode();
|
||||
unsigned target_read_length = decode();
|
||||
|
||||
if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid;
|
||||
targetlength = (source_length == source_read_length ? target_read_length : source_read_length);
|
||||
if(target_length < targetlength) return result::target_too_small;
|
||||
target_length = targetlength;
|
||||
|
||||
while(patch_offset < patch_length - 12) {
|
||||
unsigned length = decode();
|
||||
while(length--) target_write(source_read());
|
||||
while(true) {
|
||||
uint8_t patch_xor = patch_read();
|
||||
target_write(patch_xor ^ source_read());
|
||||
if(patch_xor == 0) break;
|
||||
}
|
||||
}
|
||||
while(source_offset < source_length) target_write(source_read());
|
||||
while(target_offset < target_length) target_write(source_read());
|
||||
|
||||
uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0;
|
||||
for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8);
|
||||
for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8);
|
||||
uint32_t patch_result_checksum = ~patch_checksum;
|
||||
source_checksum = ~source_checksum;
|
||||
target_checksum = ~target_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8);
|
||||
|
||||
if(patch_result_checksum != patch_read_checksum) return result::patch_invalid;
|
||||
if(source_checksum == source_read_checksum && source_length == source_read_length) {
|
||||
if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success;
|
||||
return result::target_invalid;
|
||||
} else if(source_checksum == target_read_checksum && source_length == target_read_length) {
|
||||
if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success;
|
||||
return result::target_invalid;
|
||||
} else {
|
||||
return result::source_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* patch_data = nullptr;
|
||||
uint8_t* source_data = nullptr;
|
||||
uint8_t* target_data = nullptr;
|
||||
unsigned patch_length, source_length, target_length;
|
||||
unsigned patch_offset, source_offset, target_offset;
|
||||
unsigned patch_checksum, source_checksum, target_checksum;
|
||||
file patch_file;
|
||||
|
||||
uint8_t patch_read() {
|
||||
if(patch_offset < patch_length) {
|
||||
uint8_t n = patch_data[patch_offset++];
|
||||
patch_checksum = crc32_adjust(patch_checksum, n);
|
||||
return n;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
uint8_t source_read() {
|
||||
if(source_offset < source_length) {
|
||||
uint8_t n = source_data[source_offset++];
|
||||
source_checksum = crc32_adjust(source_checksum, n);
|
||||
return n;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
uint8_t target_read() {
|
||||
uint8_t result = 0x00;
|
||||
if(target_offset < target_length) {
|
||||
result = target_data[target_offset];
|
||||
target_checksum = crc32_adjust(target_checksum, result);
|
||||
}
|
||||
if(((target_offset++ & 255) == 0) && progress) {
|
||||
progress(target_offset, source_length > target_length ? source_length : target_length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void patch_write(uint8_t n) {
|
||||
patch_file.write(n);
|
||||
patch_checksum = crc32_adjust(patch_checksum, n);
|
||||
}
|
||||
|
||||
void target_write(uint8_t n) {
|
||||
if(target_offset < target_length) {
|
||||
target_data[target_offset] = n;
|
||||
target_checksum = crc32_adjust(target_checksum, n);
|
||||
}
|
||||
if(((target_offset++ & 255) == 0) && progress) {
|
||||
progress(target_offset, source_length > target_length ? source_length : target_length);
|
||||
}
|
||||
}
|
||||
|
||||
void encode(uint64_t offset) {
|
||||
while(true) {
|
||||
uint64_t x = offset & 0x7f;
|
||||
offset >>= 7;
|
||||
if(offset == 0) {
|
||||
patch_write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
patch_write(x);
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t decode() {
|
||||
uint64_t offset = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = patch_read();
|
||||
offset += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
offset += shift;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
116
nall/utility.hpp
116
nall/utility.hpp
@ -1,9 +1,7 @@
|
||||
#ifndef NALL_UTILITY_HPP
|
||||
#define NALL_UTILITY_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/any.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
@ -12,120 +10,6 @@ template<typename T> struct base_from_member {
|
||||
base_from_member(T value) : value(value) {}
|
||||
};
|
||||
|
||||
template<typename T> struct ref {
|
||||
T& operator*() {
|
||||
if(type == Type::Reference) return *any_cast<T*>(value);
|
||||
return any_cast<T&>(value);
|
||||
}
|
||||
|
||||
operator T&() { return operator*(); }
|
||||
|
||||
ref(T& value) : type(Type::Reference), value(&value) {}
|
||||
ref(T&& value) : type(Type::Temporary), value(value) {}
|
||||
|
||||
protected:
|
||||
enum class Type : unsigned { Reference, Temporary } type;
|
||||
any value;
|
||||
};
|
||||
|
||||
template<typename TT> struct optional {
|
||||
typedef typename std::remove_reference<TT>::type T;
|
||||
static const bool isConst = std::is_const<TT>::value;
|
||||
static const bool isReference = std::is_reference<TT>::value;
|
||||
struct optional_value_not_valid{};
|
||||
|
||||
bool valid = false;
|
||||
T* value = nullptr;
|
||||
|
||||
operator bool() const { return valid; }
|
||||
|
||||
void reset() {
|
||||
valid = false;
|
||||
if(value) {
|
||||
if(!isReference) delete value;
|
||||
value = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename = typename std::enable_if<!isConst>::type>
|
||||
T& operator*() {
|
||||
if(!valid) throw optional_value_not_valid{};
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<typename = typename std::enable_if<!isConst>::type>
|
||||
T& operator()() {
|
||||
if(!valid) throw optional_value_not_valid{};
|
||||
return *value;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
if(!valid) throw optional_value_not_valid{};
|
||||
return *value;
|
||||
}
|
||||
|
||||
const T& operator()() const {
|
||||
if(!valid) throw optional_value_not_valid{};
|
||||
return *value;
|
||||
}
|
||||
|
||||
const T& operator()(const T& alternate) const {
|
||||
if(!valid) return alternate;
|
||||
return *value;
|
||||
}
|
||||
|
||||
const bool operator==(const optional& source) const {
|
||||
if(valid && source.valid) return *value == *source.value;
|
||||
if(!valid && !source.valid) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool operator!=(const optional& source) const {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
optional& operator=(const T& source) {
|
||||
reset();
|
||||
valid = true;
|
||||
if(isReference) value = (T*)&source;
|
||||
else value = new T(source);
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(T&& source) {
|
||||
reset();
|
||||
valid = true;
|
||||
if(isReference) value = &source;
|
||||
else value = new T(std::move(source));
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(const optional& source) {
|
||||
reset();
|
||||
if(source) operator=(source());
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(optional&& source) {
|
||||
reset();
|
||||
valid = source.valid;
|
||||
value = source.value;
|
||||
source.valid = false;
|
||||
source.value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional() = default;
|
||||
optional(bool valid) : valid(valid) {}
|
||||
optional(const T& value) { operator=(value); }
|
||||
optional(T&& value) { operator=(std::move(value)); }
|
||||
optional(bool valid, const T& value) : valid(valid) { if(valid) operator=(value); }
|
||||
optional(bool valid, T&& value) : valid(valid) { if(valid) operator=(std::move(value)); }
|
||||
optional(const optional& source) { operator=(source); }
|
||||
optional(optional&& source) { operator=(std::move(source)); }
|
||||
~optional() { reset(); }
|
||||
};
|
||||
|
||||
template<typename T> inline T* allocate(unsigned size, const T& value) {
|
||||
T* array = new T[size];
|
||||
for(unsigned i = 0; i < size; i++) array[i] = value;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/maybe.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
@ -164,9 +165,9 @@ public:
|
||||
nall::sort(pool + poolbase, objectsize, lessthan);
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < objectsize; n++) if(pool[poolbase + n] == data) return {true, n};
|
||||
return false;
|
||||
maybe<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < objectsize; n++) if(pool[poolbase + n] == data) return n;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
T& first() {
|
||||
|
@ -36,8 +36,8 @@ PangoFontDescription* pFont::create(string description) {
|
||||
|
||||
if(part[0] != "") family = part[0];
|
||||
if(part.size() >= 2) size = decimal(part[1]);
|
||||
if(part.size() >= 3) bold = part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = part[2].find("Italic");
|
||||
if(part.size() >= 3) bold = (bool)part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = (bool)part[2].find("Italic");
|
||||
|
||||
PangoFontDescription* font = pango_font_description_new();
|
||||
pango_font_description_set_family(font, family);
|
||||
|
@ -347,8 +347,9 @@ struct pConsole : public pWidget {
|
||||
Console& console;
|
||||
GtkWidget* subWidget;
|
||||
GtkTextBuffer* textBuffer;
|
||||
string command;
|
||||
string previousPrompt;
|
||||
lstring history;
|
||||
unsigned historyOffset = 0;
|
||||
|
||||
void print(string text);
|
||||
void reset();
|
||||
@ -359,7 +360,8 @@ struct pConsole : public pWidget {
|
||||
void destructor();
|
||||
void orphan();
|
||||
bool keyPress(unsigned scancode, unsigned mask);
|
||||
void seekCursorToEnd();
|
||||
void seekToEnd();
|
||||
void seekToMark();
|
||||
};
|
||||
|
||||
struct pFrame : public pWidget {
|
||||
|
@ -5,34 +5,28 @@ static bool Console_keyPress(GtkWidget* widget, GdkEventKey* event, Console* sel
|
||||
}
|
||||
|
||||
void pConsole::print(string text) {
|
||||
//insert text before prompt and command, so as not to interrupt the current command
|
||||
//insert text before prompt and command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_iter_set_offset(&iter, gtk_text_iter_get_offset(&iter) - console.prompt().size() - command.size());
|
||||
gtk_text_buffer_get_iter_at_line_offset(textBuffer, &iter, gtk_text_buffer_get_line_count(textBuffer), 0);
|
||||
gtk_text_buffer_insert(textBuffer, &iter, text, -1);
|
||||
seekCursorToEnd();
|
||||
seekToEnd();
|
||||
}
|
||||
|
||||
void pConsole::reset() {
|
||||
//flush history and command; draw prompt
|
||||
command.reset();
|
||||
//flush history and redraw prompt
|
||||
gtk_text_buffer_set_text(textBuffer, console.prompt(), -1);
|
||||
seekCursorToEnd();
|
||||
seekToEnd();
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
//erase old prompt; insert new prompt in its place
|
||||
GtkTextIter lhs, rhs, iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - previousPrompt.size() - command.size());
|
||||
gtk_text_iter_set_offset(&rhs, gtk_text_iter_get_offset(&rhs) - command.size());
|
||||
//erase previous prompt and replace it with new prompt
|
||||
GtkTextIter lhs, rhs;
|
||||
gtk_text_buffer_get_iter_at_line_offset(textBuffer, &lhs, gtk_text_buffer_get_line_count(textBuffer), 0);
|
||||
gtk_text_buffer_get_iter_at_line_offset(textBuffer, &rhs, gtk_text_buffer_get_line_count(textBuffer), previousPrompt.size());
|
||||
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_iter_set_offset(&iter, gtk_text_iter_get_offset(&iter) - command.size());
|
||||
gtk_text_buffer_insert(textBuffer, &iter, prompt, -1);
|
||||
seekCursorToEnd();
|
||||
previousPrompt = prompt;
|
||||
gtk_text_buffer_get_iter_at_line_offset(textBuffer, &lhs, gtk_text_buffer_get_line_count(textBuffer), 0);
|
||||
gtk_text_buffer_insert(textBuffer, &lhs, previousPrompt = prompt, -1);
|
||||
seekToEnd();
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
@ -47,7 +41,7 @@ void pConsole::constructor() {
|
||||
|
||||
textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
|
||||
|
||||
GdkColor background = CreateColor(48, 24, 24);
|
||||
GdkColor background = CreateColor(72, 24, 24);
|
||||
gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, &background);
|
||||
GdkColor foreground = CreateColor(255, 255, 255);
|
||||
gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, &foreground);
|
||||
@ -70,50 +64,116 @@ void pConsole::orphan() {
|
||||
bool pConsole::keyPress(unsigned scancode, unsigned mask) {
|
||||
if(mask & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK)) return false; //allow actions such as Ctrl+C (copy)
|
||||
|
||||
GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
|
||||
GtkTextIter start, cursor, end;
|
||||
gtk_text_buffer_get_iter_at_line_offset(textBuffer, &start, gtk_text_buffer_get_line_count(textBuffer), console.prompt().size());
|
||||
gtk_text_buffer_get_iter_at_mark(textBuffer, &cursor, mark);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &end);
|
||||
|
||||
if(scancode == GDK_KEY_Return || scancode == GDK_KEY_KP_Enter) {
|
||||
//add current prompt and command to history; print new prompt; execute command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_insert(textBuffer, &iter, string{"\n", console.prompt()}, -1);
|
||||
string s = command;
|
||||
command.reset();
|
||||
char* temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true);
|
||||
string s = temp;
|
||||
g_free(temp);
|
||||
gtk_text_buffer_insert(textBuffer, &end, string{"\n", console.prompt()}, -1);
|
||||
if(console.onActivate) console.onActivate(s);
|
||||
seekCursorToEnd();
|
||||
if(s) history.prepend(s);
|
||||
if(history.size() > 128) history.removeLast();
|
||||
historyOffset = 0;
|
||||
seekToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Up) {
|
||||
gtk_text_buffer_delete(textBuffer, &start, &end);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &end);
|
||||
if(historyOffset < history.size()) {
|
||||
gtk_text_buffer_insert(textBuffer, &end, history[historyOffset++], -1);
|
||||
}
|
||||
seekToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Down) {
|
||||
gtk_text_buffer_delete(textBuffer, &start, &end);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &end);
|
||||
if(historyOffset > 0) {
|
||||
gtk_text_buffer_insert(textBuffer, &end, history[--historyOffset], -1);
|
||||
}
|
||||
seekToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Left) {
|
||||
if(gtk_text_iter_get_offset(&cursor) <= gtk_text_iter_get_offset(&start)) {
|
||||
gtk_text_buffer_place_cursor(textBuffer, &start);
|
||||
} else {
|
||||
gtk_text_iter_set_offset(&cursor, gtk_text_iter_get_offset(&cursor) - 1);
|
||||
gtk_text_buffer_place_cursor(textBuffer, &cursor);
|
||||
}
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Right) {
|
||||
if(gtk_text_iter_get_offset(&cursor) < gtk_text_iter_get_offset(&start)) {
|
||||
gtk_text_buffer_place_cursor(textBuffer, &end);
|
||||
} else if(gtk_text_iter_get_offset(&cursor) < gtk_text_iter_get_offset(&end)) {
|
||||
gtk_text_iter_set_offset(&cursor, gtk_text_iter_get_offset(&cursor) + 1);
|
||||
gtk_text_buffer_place_cursor(textBuffer, &cursor);
|
||||
}
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Home) {
|
||||
gtk_text_buffer_place_cursor(textBuffer, &start);
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_End) {
|
||||
gtk_text_buffer_place_cursor(textBuffer, &end);
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_BackSpace) {
|
||||
if(command.size()) {
|
||||
//delete last character of command
|
||||
command.resize(command.size() - 1);
|
||||
GtkTextIter lhs, rhs;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - 1);
|
||||
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
||||
}
|
||||
seekCursorToEnd();
|
||||
if(gtk_text_iter_get_offset(&cursor) <= gtk_text_iter_get_offset(&start)) return true;
|
||||
GtkTextIter lhs = cursor;
|
||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&cursor) - 1);
|
||||
gtk_text_buffer_delete(textBuffer, &lhs, &cursor);
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Delete) {
|
||||
if(gtk_text_iter_get_offset(&cursor) < gtk_text_iter_get_offset(&start)) return true;
|
||||
if(gtk_text_iter_get_offset(&cursor) == gtk_text_iter_get_offset(&end)) return true;
|
||||
GtkTextIter rhs = cursor;
|
||||
gtk_text_iter_set_offset(&rhs, gtk_text_iter_get_offset(&cursor) + 1);
|
||||
gtk_text_buffer_delete(textBuffer, &cursor, &rhs);
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode >= 0x20 && scancode <= 0x7e) {
|
||||
//add character to end of command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_insert(textBuffer, &iter, string{(char)scancode}, -1);
|
||||
seekCursorToEnd();
|
||||
command.append((char)scancode);
|
||||
if(gtk_text_iter_get_offset(&cursor) < gtk_text_iter_get_offset(&start)) return true;
|
||||
gtk_text_buffer_insert(textBuffer, &cursor, string{(char)scancode}, -1);
|
||||
seekToMark();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void pConsole::seekCursorToEnd() {
|
||||
//place cursor at end of text buffer; scroll text view to the cursor to ensure it is visible
|
||||
void pConsole::seekToEnd() {
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_place_cursor(textBuffer, &iter);
|
||||
seekToMark();
|
||||
}
|
||||
|
||||
void pConsole::seekToMark() {
|
||||
GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
|
||||
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ QFont pFont::create(string description) {
|
||||
|
||||
if(part[0] != "") family = part[0];
|
||||
if(part.size() >= 2) size = decimal(part[1]);
|
||||
if(part.size() >= 3) bold = part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = part[2].find("Italic");
|
||||
if(part.size() >= 3) bold = (bool)part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = (bool)part[2].find("Italic");
|
||||
|
||||
QFont qtFont;
|
||||
qtFont.setFamily(family);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||
**
|
||||
** Created: Wed Jan 29 08:08:49 2014
|
||||
** Created: Wed Feb 5 08:24:19 2014
|
||||
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
@ -37,8 +37,8 @@ HFONT pFont::create(string description) {
|
||||
|
||||
if(part[0] != "") family = part[0];
|
||||
if(part.size() >= 2) size = decimal(part[1]);
|
||||
if(part.size() >= 3) bold = part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = part[2].find("Italic");
|
||||
if(part.size() >= 3) bold = (bool)part[2].find("Bold");
|
||||
if(part.size() >= 3) italic = (bool)part[2].find("Italic");
|
||||
|
||||
return CreateFont(
|
||||
-(size * 96.0 / 72.0 + 0.5),
|
||||
|
@ -1,4 +1,4 @@
|
||||
string SPC700::disassemble_opcode(uint16 addr) {
|
||||
string SPC700::disassemble_opcode(uint16 addr, bool p) {
|
||||
auto read = [&](uint16 addr) -> uint8 {
|
||||
return disassembler_read(addr);
|
||||
};
|
||||
@ -11,7 +11,7 @@ string SPC700::disassemble_opcode(uint16 addr) {
|
||||
auto a = [&] { return hex<4>((read(addr + 1) << 0) + (read(addr + 2) << 8)); };
|
||||
auto b = [&](unsigned n) { return hex<2>(read(addr + 1 + n)); };
|
||||
auto r = [&](unsigned r, unsigned n = 0) { return hex<4>(addr + r + (int8)read(addr + 1 + n)); };
|
||||
auto dp = [&](unsigned n) { return hex<3>((regs.p.p << 8) + read(addr + 1 + n)); };
|
||||
auto dp = [&](unsigned n) { return hex<3>((p << 8) + read(addr + 1 + n)); };
|
||||
auto ab = [&] {
|
||||
unsigned n = (read(addr + 1) << 0) + (read(addr + 2) << 8);
|
||||
return string{ hex<4>(n & 0x1fff), ":", hex<1>(n >> 13) };
|
||||
|
@ -19,7 +19,7 @@ struct SPC700 {
|
||||
uint8 opcode;
|
||||
|
||||
void serialize(serializer&);
|
||||
string disassemble_opcode(uint16 addr);
|
||||
string disassemble_opcode(uint16 addr, bool p);
|
||||
|
||||
protected:
|
||||
uint8 op_adc(uint8, uint8);
|
||||
|
@ -17,16 +17,16 @@ void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
|
||||
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
|
||||
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return {true, code.data};
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ struct Cheat {
|
||||
void reset();
|
||||
void append(unsigned addr, unsigned data);
|
||||
void append(unsigned addr, unsigned comp, unsigned data);
|
||||
optional<unsigned> find(unsigned addr, unsigned comp);
|
||||
maybe<unsigned> find(unsigned addr, unsigned comp);
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
|
@ -86,12 +86,6 @@ void CPU::enter() {
|
||||
|
||||
void CPU::op_step() {
|
||||
debugger.op_exec(regs.pc.d);
|
||||
if(interface->tracer.open()) {
|
||||
char text[4096];
|
||||
disassemble_opcode(text);
|
||||
interface->tracer.print(text, "\n");
|
||||
}
|
||||
|
||||
(this->*opcode_table[op_readpc()])();
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,6 @@ void Interface::save(unsigned id, const stream& stream) {
|
||||
void Interface::unload() {
|
||||
save();
|
||||
cartridge.unload();
|
||||
tracerEnable(false);
|
||||
}
|
||||
|
||||
void Interface::connect(unsigned port, unsigned device) {
|
||||
@ -341,38 +340,6 @@ void Interface::paletteUpdate(PaletteMode mode) {
|
||||
video.generate_palette(mode);
|
||||
}
|
||||
|
||||
bool Interface::tracerEnable(bool trace) {
|
||||
string pathname = {path(group(ID::ROM)), "debug/"};
|
||||
if(trace == true) directory::create(pathname);
|
||||
|
||||
if(trace == true && !tracer.open()) {
|
||||
for(unsigned n = 0; n <= 999; n++) {
|
||||
string filename = {pathname, "trace-", format<3, '0'>(n), ".log"};
|
||||
if(file::exists(filename)) continue;
|
||||
tracer.open(filename, file::mode::write);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(trace == false && tracer.open()) {
|
||||
tracer.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Interface::exportMemory() {
|
||||
string pathname = {path(group(ID::ROM)), "debug/"};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "work.ram"}, cpu.wram, 128 * 1024);
|
||||
file::write({pathname, "video.ram"}, ppu.vram, 64 * 1024);
|
||||
file::write({pathname, "sprite.ram"}, ppu.oam, 544);
|
||||
file::write({pathname, "palette.ram"}, ppu.cgram, 512);
|
||||
file::write({pathname, "apu.ram"}, smp.apuram, 64 * 1024);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
system.init();
|
||||
|
@ -117,13 +117,8 @@ struct Interface : Emulator::Interface {
|
||||
|
||||
void paletteUpdate(PaletteMode mode);
|
||||
|
||||
//debugger functions
|
||||
bool tracerEnable(bool);
|
||||
void exportMemory();
|
||||
|
||||
Interface();
|
||||
|
||||
file tracer;
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
|
@ -129,28 +129,6 @@ void InputManager::appendHotkeys() {
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Toggle Tracer";
|
||||
hotkey->mapping = "None";
|
||||
|
||||
hotkey->press = [&] {
|
||||
utility->tracerToggle();
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Export Memory";
|
||||
hotkey->mapping = "None";
|
||||
|
||||
hotkey->press = [&] {
|
||||
if(program->active == nullptr) return;
|
||||
system().exportMemory();
|
||||
utility->showMessage("Memory exported");
|
||||
};
|
||||
}
|
||||
|
||||
Configuration::Node node;
|
||||
for(auto& hotkey : hotkeyMap) {
|
||||
node.append(hotkey->mapping, string{hotkey->name}.replace(" ", ""));
|
||||
|
@ -101,7 +101,6 @@ void Utility::load() {
|
||||
|
||||
void Utility::unload() {
|
||||
if(program->active == nullptr) return;
|
||||
if(tracerEnable) tracerToggle();
|
||||
|
||||
cheatEditor->save({pathname[0], "cheats.bml"});
|
||||
stateManager->save({pathname[0], "higan/states.bsa"}, 1);
|
||||
@ -139,16 +138,6 @@ void Utility::loadState(unsigned slot) {
|
||||
showMessage({"Loaded from slot ", slot});
|
||||
}
|
||||
|
||||
void Utility::tracerToggle() {
|
||||
if(program->active == nullptr) return;
|
||||
tracerEnable = !tracerEnable;
|
||||
bool result = system().tracerEnable(tracerEnable);
|
||||
if( tracerEnable && result) return utility->showMessage("Tracer activated");
|
||||
if( tracerEnable && !result) return tracerEnable = false, utility->showMessage("Unable to activate tracer");
|
||||
if(!tracerEnable && result) return utility->showMessage("Tracer deactivated");
|
||||
if(!tracerEnable && !result) return utility->showMessage("Unable to deactivate tracer");
|
||||
}
|
||||
|
||||
void Utility::synchronizeDSP() {
|
||||
if(program->active == nullptr) return;
|
||||
|
||||
@ -323,8 +312,3 @@ string Utility::libraryPath() {
|
||||
if(path.endsWith("/") == false) path.append("/");
|
||||
return path;
|
||||
}
|
||||
|
||||
Utility::Utility() {
|
||||
tracerEnable = false;
|
||||
statusTime = 0;
|
||||
}
|
||||
|
@ -17,8 +17,6 @@ struct Utility {
|
||||
void saveState(unsigned slot);
|
||||
void loadState(unsigned slot);
|
||||
|
||||
void tracerToggle();
|
||||
|
||||
void synchronizeDSP();
|
||||
void synchronizeRuby();
|
||||
void updatePalette();
|
||||
@ -32,16 +30,13 @@ struct Utility {
|
||||
|
||||
string libraryPath();
|
||||
|
||||
Utility();
|
||||
|
||||
lstring path;
|
||||
lstring pathname;
|
||||
|
||||
private:
|
||||
bool tracerEnable;
|
||||
string statusText;
|
||||
string statusMessage;
|
||||
time_t statusTime;
|
||||
time_t statusTime = 0;
|
||||
};
|
||||
|
||||
extern Utility* utility;
|
||||
|
@ -20,37 +20,30 @@ Debugger::Debugger() {
|
||||
void Debugger::load() {
|
||||
directory::create({interface->pathname, "loki/"});
|
||||
|
||||
usageCPU = new uint8[0x1000000]();
|
||||
usageAPU = new uint8[0x10000]();
|
||||
cpuUsage = new uint8[0x1000000]();
|
||||
apuUsage = new uint8[0x10000]();
|
||||
file fp;
|
||||
|
||||
if(fp.open({interface->pathname, "loki/usage.cpu.bin"}, file::mode::read)) {
|
||||
if(fp.size() == 0x1000000) fp.read(usageCPU, 0x1000000);
|
||||
if(fp.open({interface->pathname, "loki/cpu.usage.map"}, file::mode::read)) {
|
||||
if(fp.size() == 0x1000000) fp.read(cpuUsage, 0x1000000);
|
||||
fp.close();
|
||||
}
|
||||
|
||||
if(fp.open({interface->pathname, "loki/usage.apu.bin"}, file::mode::read)) {
|
||||
if(fp.size() == 0x10000) fp.read(usageAPU, 0x10000);
|
||||
if(fp.open({interface->pathname, "loki/apu.usage.map"}, file::mode::read)) {
|
||||
if(fp.size() == 0x10000) fp.read(apuUsage, 0x10000);
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::unload() {
|
||||
if(tracerFile.open()) tracerFile.close();
|
||||
|
||||
file fp;
|
||||
if(fp.open({interface->pathname, "loki/usage.cpu.bin"}, file::mode::write)) {
|
||||
fp.write(usageCPU, 0x1000000);
|
||||
fp.close();
|
||||
}
|
||||
if(fp.open({interface->pathname, "loki/usage.apu.bin"}, file::mode::write)) {
|
||||
fp.write(usageAPU, 0x10000);
|
||||
fp.close();
|
||||
}
|
||||
delete[] usageCPU;
|
||||
delete[] usageAPU;
|
||||
usageCPU = nullptr;
|
||||
usageAPU = nullptr;
|
||||
if(cpuTracerFile.open()) cpuTracerFile.close();
|
||||
if(smpTracerFile.open()) smpTracerFile.close();
|
||||
file::write({interface->pathname, "loki/cpu.usage.map"}, cpuUsage, 0x1000000);
|
||||
file::write({interface->pathname, "loki/apu.usage.map"}, apuUsage, 0x10000);
|
||||
delete[] cpuUsage;
|
||||
delete[] apuUsage;
|
||||
cpuUsage = nullptr;
|
||||
apuUsage = nullptr;
|
||||
}
|
||||
|
||||
void Debugger::main() {
|
||||
@ -68,10 +61,14 @@ void Debugger::run() {
|
||||
|
||||
void Debugger::stop() {
|
||||
running = false;
|
||||
cpuRunFor.reset();
|
||||
cpuRunTo.reset();
|
||||
cpuStepFor.reset();
|
||||
cpuStepTo.reset();
|
||||
cpuRunFor = nothing;
|
||||
cpuRunTo = nothing;
|
||||
cpuStepFor = nothing;
|
||||
cpuStepTo = nothing;
|
||||
smpRunFor = nothing;
|
||||
smpRunTo = nothing;
|
||||
smpStepFor = nothing;
|
||||
smpStepTo = nothing;
|
||||
}
|
||||
|
||||
void Debugger::leave() {
|
||||
@ -86,10 +83,11 @@ bool Debugger::breakpointTest(Source source, Breakpoint::Mode mode, unsigned add
|
||||
if(bp.mode != mode) continue;
|
||||
if(bp.addr != addr) continue;
|
||||
if(bp.mode != Breakpoint::Mode::Execute && bp.data && bp.data() != data) continue;
|
||||
echo("Breakpoint #", n, " hit");
|
||||
if(bp.mode == Breakpoint::Mode::Read ) echo("; read ", hex<2>(data));
|
||||
if(bp.mode == Breakpoint::Mode::Write) echo("; wrote ", hex<2>(data));
|
||||
echo("; triggered: ", ++bp.triggered, "\n");
|
||||
string output = {"Breakpoint #", n, " hit"};
|
||||
if(bp.mode == Breakpoint::Mode::Read ) output.append("; read ", hex<2>(data));
|
||||
if(bp.mode == Breakpoint::Mode::Write) output.append("; wrote ", hex<2>(data));
|
||||
output.append("; triggered: ", ++bp.triggered);
|
||||
echo(output, "\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -98,28 +96,30 @@ bool Debugger::breakpointTest(Source source, Breakpoint::Mode mode, unsigned add
|
||||
string Debugger::cpuDisassemble() {
|
||||
char text[4096];
|
||||
SFC::cpu.disassemble_opcode(text);
|
||||
return {text, " I:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
|
||||
return {text, " F:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
|
||||
}
|
||||
|
||||
string Debugger::cpuDisassemble(unsigned addr, bool e, bool m, bool x) {
|
||||
char text[4096];
|
||||
SFC::cpu.disassemble_opcode(text, addr, e, m, x);
|
||||
return {text, " I:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
|
||||
return {text, " F:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
|
||||
}
|
||||
|
||||
void Debugger::cpuExec(uint24 addr) {
|
||||
usageCPU[addr] |= Usage::Execute;
|
||||
if(SFC::cpu.regs.e == 0) usageCPU[addr] &= ~Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 0) usageCPU[addr] &= ~Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 0) usageCPU[addr] &= ~Usage::FlagX;
|
||||
if(SFC::cpu.regs.e == 1) usageCPU[addr] |= Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 1) usageCPU[addr] |= Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 1) usageCPU[addr] |= Usage::FlagX;
|
||||
cpuUsage[addr] |= Usage::Execute;
|
||||
if(SFC::cpu.regs.e == 0) cpuUsage[addr] &= ~Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 0) cpuUsage[addr] &= ~Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 0) cpuUsage[addr] &= ~Usage::FlagX;
|
||||
if(SFC::cpu.regs.e == 1) cpuUsage[addr] |= Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 1) cpuUsage[addr] |= Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 1) cpuUsage[addr] |= Usage::FlagX;
|
||||
|
||||
if(tracerFile.open()) {
|
||||
if(!tracerMask || tracerMask[addr] == false) {
|
||||
if(tracerMask) tracerMask[addr] = true;
|
||||
tracerFile.print(cpuDisassemble(), "\n");
|
||||
cpuInstructionCounter++;
|
||||
|
||||
if(cpuTracerFile.open()) {
|
||||
if(!cpuTracerMask || cpuTracerMask[addr] == false) {
|
||||
if(cpuTracerMask) cpuTracerMask[addr] = true;
|
||||
cpuTracerFile.print(cpuDisassemble(), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,12 +154,12 @@ void Debugger::cpuExec(uint24 addr) {
|
||||
}
|
||||
|
||||
void Debugger::cpuRead(uint24 addr, uint8 data) {
|
||||
usageCPU[addr] |= Usage::Read;
|
||||
cpuUsage[addr] |= Usage::Read;
|
||||
if(breakpointTest(Source::CPU, Breakpoint::Mode::Read, addr, data)) leave();
|
||||
}
|
||||
|
||||
void Debugger::cpuWrite(uint24 addr, uint8 data) {
|
||||
usageCPU[addr] |= Usage::Write;
|
||||
cpuUsage[addr] |= Usage::Write;
|
||||
if(breakpointTest(Source::CPU, Breakpoint::Mode::Write, addr, data)) leave();
|
||||
}
|
||||
|
||||
@ -182,21 +182,30 @@ void Debugger::echoBreakpoints() {
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::echoDisassemble(unsigned addr, signed size) {
|
||||
if(!(usageCPU[addr] & Usage::Execute)) return echo("No usage data available for cpu/", hex<6>(addr), "\n");
|
||||
void Debugger::echoDisassemble(Source source, unsigned addr, signed size) {
|
||||
if(source != Source::CPU && source != Source::SMP) return;
|
||||
const unsigned maximumDisplacement = (source == Source::CPU ? 5 : 4); //maximum opcode length
|
||||
uint8* usage = (source == Source::CPU ? cpuUsage : apuUsage);
|
||||
if(!(usage[addr] & Usage::Execute)) return echo("No usage data available for ", sourceName(source), "/", hex<6>(addr), "\n");
|
||||
|
||||
while(size > 0) {
|
||||
string text = cpuDisassemble(addr, usageCPU[addr] & Usage::FlagE, usageCPU[addr] & Usage::FlagM, usageCPU[addr] & Usage::FlagX);
|
||||
string text;
|
||||
if(source == Source::CPU) {
|
||||
text = cpuDisassemble(addr, usage[addr] & Usage::FlagE, usage[addr] & Usage::FlagM, usage[addr] & Usage::FlagX);
|
||||
}
|
||||
if(source == Source::SMP) {
|
||||
text = smpDisassemble(addr, usage[addr] & Usage::FlagP);
|
||||
}
|
||||
text.resize(20); //remove register information
|
||||
echo(text, "\n");
|
||||
if(--size <= 0) break;
|
||||
|
||||
unsigned displacement = 1;
|
||||
while(displacement < 5) { //maximum opcode length is four bytes
|
||||
if(usageCPU[addr + displacement] & Usage::Execute) break;
|
||||
while(displacement < maximumDisplacement) { //maximum opcode length is four bytes
|
||||
if(usage[addr + displacement] & Usage::Execute) break;
|
||||
displacement++;
|
||||
}
|
||||
if(displacement >= 5) {
|
||||
if(displacement >= maximumDisplacement) {
|
||||
echo("...\n");
|
||||
return;
|
||||
}
|
||||
@ -205,6 +214,7 @@ void Debugger::echoDisassemble(unsigned addr, signed size) {
|
||||
}
|
||||
|
||||
void Debugger::echoHex(Source source, unsigned addr, signed size) {
|
||||
if(memorySize(source) == 0) return; //not a valid memory pool
|
||||
while(size > 0) {
|
||||
string hexdata, asciidata;
|
||||
for(unsigned n = 0; n < 16; n++) {
|
||||
@ -245,6 +255,10 @@ uint8 Debugger::memoryRead(Source source, unsigned addr) {
|
||||
return SFC::smp.apuram[addr & 0xffff];
|
||||
}
|
||||
|
||||
if(source == Source::WRAM) {
|
||||
return SFC::cpu.wram[addr & 0x1ffff];
|
||||
}
|
||||
|
||||
if(source == Source::VRAM) {
|
||||
return SFC::ppu.vram[addr & 0xffff];
|
||||
}
|
||||
@ -264,11 +278,12 @@ unsigned Debugger::memorySize(Source source) {
|
||||
switch(source) {
|
||||
case Source::CPU: return 0x1000000;
|
||||
case Source::APU: return 0x10000;
|
||||
case Source::WRAM: return 0x20000;
|
||||
case Source::VRAM: return 0x10000;
|
||||
case Source::OAM: return 544;
|
||||
case Source::CGRAM: return 512;
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Debugger::memoryWrite(Source source, unsigned addr, uint8 data) {
|
||||
@ -282,6 +297,11 @@ void Debugger::memoryWrite(Source source, unsigned addr, uint8 data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(source == Source::WRAM) {
|
||||
SFC::cpu.wram[addr & 0x1ffff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(source == Source::VRAM) {
|
||||
SFC::ppu.vram[addr & 0xffff] = data;
|
||||
return;
|
||||
@ -324,22 +344,66 @@ void Debugger::ppuVramWrite(uint16 addr, uint8 data) {
|
||||
if(breakpointTest(Source::VRAM, Breakpoint::Mode::Write, addr, data)) leave();
|
||||
}
|
||||
|
||||
string Debugger::smpDisassemble() {
|
||||
return SFC::smp.disassemble_opcode(SFC::smp.regs.pc, SFC::smp.regs.p.p);
|
||||
}
|
||||
|
||||
string Debugger::smpDisassemble(uint16 addr, bool p) {
|
||||
return SFC::smp.disassemble_opcode(addr, p);
|
||||
}
|
||||
|
||||
void Debugger::smpExec(uint16 addr) {
|
||||
usageAPU[addr] |= Usage::Execute;
|
||||
apuUsage[addr] |= Usage::Execute;
|
||||
if(SFC::smp.regs.p.p == 0) apuUsage[addr] &= ~Usage::FlagP;
|
||||
if(SFC::smp.regs.p.p == 1) apuUsage[addr] |= Usage::FlagP;
|
||||
|
||||
smpInstructionCounter++;
|
||||
|
||||
if(smpTracerFile.open()) {
|
||||
if(!smpTracerMask || smpTracerMask[addr] == false) {
|
||||
if(smpTracerMask) smpTracerMask[addr] = true;
|
||||
smpTracerFile.print(smpDisassemble(), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(breakpointTest(Source::SMP, Breakpoint::Mode::Execute, addr)) {
|
||||
leave();
|
||||
echo(smpDisassemble(), "\n");
|
||||
return leave();
|
||||
}
|
||||
|
||||
if(smpRunFor) {
|
||||
if(--smpRunFor() == 0) {
|
||||
echo(smpDisassemble(), "\n");
|
||||
return leave();
|
||||
}
|
||||
}
|
||||
|
||||
if(smpRunTo) {
|
||||
if(addr == smpRunTo()) {
|
||||
echo(smpDisassemble(), "\n");
|
||||
return leave();
|
||||
}
|
||||
}
|
||||
|
||||
if(smpStepFor) {
|
||||
echo(smpDisassemble(), "\n");
|
||||
if(--smpStepFor() == 0) return leave();
|
||||
}
|
||||
|
||||
if(smpStepTo) {
|
||||
echo(smpDisassemble(), "\n");
|
||||
if(addr == smpStepTo()) return leave();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::smpRead(uint16 addr, uint8 data) {
|
||||
usageAPU[addr] |= Usage::Read;
|
||||
apuUsage[addr] |= Usage::Read;
|
||||
if(breakpointTest(Source::SMP, Breakpoint::Mode::Read, addr, data)) leave();
|
||||
if(breakpointTest(Source::APU, Breakpoint::Mode::Read, addr, data)) leave();
|
||||
}
|
||||
|
||||
void Debugger::smpWrite(uint16 addr, uint8 data) {
|
||||
usageAPU[addr] |= Usage::Write;
|
||||
apuUsage[addr] |= Usage::Write;
|
||||
if(breakpointTest(Source::SMP, Breakpoint::Mode::Write, addr, data)) leave();
|
||||
if(breakpointTest(Source::APU, Breakpoint::Mode::Write, addr, data)) leave();
|
||||
}
|
||||
@ -351,9 +415,43 @@ string Debugger::sourceName(Source source) {
|
||||
case Source::PPU: return "ppu";
|
||||
case Source::DSP: return "dsp";
|
||||
case Source::APU: return "apu";
|
||||
case Source::WRAM: return "wram";
|
||||
case Source::VRAM: return "vram";
|
||||
case Source::OAM: return "oam";
|
||||
case Source::CGRAM: return "cgram";
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
void Debugger::tracerDisable(Source source) {
|
||||
if(source != Source::CPU && source != Source::SMP) return;
|
||||
file& tracerFile = (source == Source::CPU ? cpuTracerFile : smpTracerFile);
|
||||
if(tracerFile.open() == false) return;
|
||||
tracerFile.close();
|
||||
echo(sourceName(source).upper(), " tracer disabled\n");
|
||||
}
|
||||
|
||||
void Debugger::tracerEnable(Source source, string filename) {
|
||||
if(source != Source::CPU && source != Source::SMP) return;
|
||||
file& tracerFile = (source == Source::CPU ? cpuTracerFile : smpTracerFile);
|
||||
if(tracerFile.open() == true) return;
|
||||
if(tracerFile.open(filename, file::mode::write)) {
|
||||
echo(sourceName(source).upper(), " tracer enabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::tracerMaskDisable(Source source) {
|
||||
if(source != Source::CPU && source != Source::SMP) return;
|
||||
bitvector& tracerMask = (source == Source::CPU ? cpuTracerMask : smpTracerMask);
|
||||
tracerMask.reset();
|
||||
echo(sourceName(source).upper(), " tracer mask disabled\n");
|
||||
}
|
||||
|
||||
void Debugger::tracerMaskEnable(Source source) {
|
||||
if(source != Source::CPU && source != Source::SMP) return;
|
||||
bitvector& tracerMask = (source == Source::CPU ? cpuTracerMask : smpTracerMask);
|
||||
unsigned size = (source == Source::CPU ? 0x1000000 : 0x10000);
|
||||
tracerMask.resize(size);
|
||||
tracerMask.clear();
|
||||
echo(sourceName(source).upper(), " tracer mask enabled\n");
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
struct Debugger {
|
||||
enum class Source : unsigned { CPU, SMP, PPU, DSP, APU, VRAM, OAM, CGRAM };
|
||||
enum class Source : unsigned { CPU, SMP, PPU, DSP, APU, WRAM, VRAM, OAM, CGRAM };
|
||||
|
||||
struct Breakpoint {
|
||||
Source source = Source::CPU;
|
||||
enum class Mode : unsigned { Disabled, Read, Write, Execute } mode = Mode::Disabled;
|
||||
unsigned addr = 0;
|
||||
optional<uint8> data = false;
|
||||
maybe<uint8> data;
|
||||
unsigned triggered = 0; //counter for number of times breakpoint was hit
|
||||
};
|
||||
|
||||
@ -19,8 +19,9 @@ struct Debugger {
|
||||
FlagM = 0x10,
|
||||
FlagX = 0x20,
|
||||
//APU
|
||||
DspRead = 0x08,
|
||||
DspWrite = 0x10,
|
||||
FlagP = 0x08,
|
||||
DspRead = 0x10,
|
||||
DspWrite = 0x20,
|
||||
};
|
||||
};
|
||||
|
||||
@ -41,7 +42,7 @@ struct Debugger {
|
||||
void cpuRead(uint24 addr, uint8 data);
|
||||
void cpuWrite(uint24 addr, uint8 data);
|
||||
void echoBreakpoints();
|
||||
void echoDisassemble(unsigned addr, signed size);
|
||||
void echoDisassemble(Source source, unsigned addr, signed size);
|
||||
void echoHex(Source source, unsigned addr, signed size);
|
||||
void memoryExport(Source source, string filename);
|
||||
uint8 memoryRead(Source source, unsigned addr);
|
||||
@ -53,22 +54,36 @@ struct Debugger {
|
||||
void ppuOamWrite(uint16 addr, uint8 data);
|
||||
void ppuVramRead(uint16 addr, uint8 data);
|
||||
void ppuVramWrite(uint16 addr, uint8 data);
|
||||
string smpDisassemble();
|
||||
string smpDisassemble(uint16 addr, bool p);
|
||||
void smpExec(uint16 addr);
|
||||
void smpRead(uint16 addr, uint8 data);
|
||||
void smpWrite(uint16 addr, uint8 data);
|
||||
string sourceName(Source source);
|
||||
void tracerDisable(Source source);
|
||||
void tracerEnable(Source source, string filename);
|
||||
void tracerMaskDisable(Source source);
|
||||
void tracerMaskEnable(Source source);
|
||||
|
||||
bool running = false;
|
||||
|
||||
uint8* apuUsage = nullptr;
|
||||
vector<Breakpoint> breakpoints;
|
||||
optional<unsigned> cpuRunFor = false;
|
||||
optional<unsigned> cpuRunTo = false;
|
||||
optional<unsigned> cpuStepFor = false;
|
||||
optional<unsigned> cpuStepTo = false;
|
||||
file tracerFile;
|
||||
bitvector tracerMask;
|
||||
uint8* usageCPU = nullptr;
|
||||
uint8* usageAPU = nullptr;
|
||||
unsigned cpuInstructionCounter = 0;
|
||||
maybe<unsigned> cpuRunFor;
|
||||
maybe<unsigned> cpuRunTo;
|
||||
maybe<unsigned> cpuStepFor;
|
||||
maybe<unsigned> cpuStepTo;
|
||||
file cpuTracerFile;
|
||||
bitvector cpuTracerMask;
|
||||
uint8* cpuUsage = nullptr;
|
||||
unsigned smpInstructionCounter = 0;
|
||||
maybe<unsigned> smpRunFor;
|
||||
maybe<unsigned> smpRunTo;
|
||||
maybe<unsigned> smpStepFor;
|
||||
maybe<unsigned> smpStepTo;
|
||||
file smpTracerFile;
|
||||
bitvector smpTracerMask;
|
||||
};
|
||||
|
||||
extern Debugger* debugger;
|
||||
|
@ -24,6 +24,7 @@ void Terminal::command(string t) {
|
||||
if(t.beginsWith("ppu/" )) { source = Debugger::Source::PPU; t.ltrim<1>("ppu/" ); }
|
||||
if(t.beginsWith("dsp/" )) { source = Debugger::Source::DSP; t.ltrim<1>("dsp/" ); }
|
||||
if(t.beginsWith("apu/" )) { source = Debugger::Source::APU; t.ltrim<1>("apu/" ); }
|
||||
if(t.beginsWith("wram/" )) { source = Debugger::Source::WRAM; t.ltrim<1>("wram/" ); }
|
||||
if(t.beginsWith("vram/" )) { source = Debugger::Source::VRAM; t.ltrim<1>("vram/" ); }
|
||||
if(t.beginsWith("oam/" )) { source = Debugger::Source::OAM; t.ltrim<1>("oam/" ); }
|
||||
if(t.beginsWith("cgram/")) { source = Debugger::Source::CGRAM; t.ltrim<1>("cgram/"); }
|
||||
@ -56,7 +57,7 @@ void Terminal::command(string t) {
|
||||
if(args[0] == "write" ) bp.mode = Debugger::Breakpoint::Mode::Write;
|
||||
if(args[0] == "execute") bp.mode = Debugger::Breakpoint::Mode::Execute;
|
||||
bp.addr = hex(args[1]);
|
||||
if(argc >= 3) bp.data = {true, (uint8_t)hex(args[2])};
|
||||
if(argc >= 3) bp.data = (uint8_t)hex(args[2]);
|
||||
debugger->breakpoints.append(bp);
|
||||
debugger->echoBreakpoints();
|
||||
return;
|
||||
@ -81,13 +82,25 @@ void Terminal::command(string t) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "counter") {
|
||||
if(source == Debugger::Source::CPU) echo("CPU instructions executed: ", debugger->cpuInstructionCounter, "\n");
|
||||
if(source == Debugger::Source::SMP) echo("SMP instructions executed: ", debugger->smpInstructionCounter, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "counter.reset") {
|
||||
if(source == Debugger::Source::CPU) { echo("CPU instruction counter reset\n"); debugger->cpuInstructionCounter = 0; }
|
||||
if(source == Debugger::Source::SMP) { echo("SMP instruction counter reset\n"); debugger->smpInstructionCounter = 0; }
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "disassemble" && argc >= 1 && argc <= 2) {
|
||||
debugger->echoDisassemble(hex(args[0]), argc == 2 ? decimal(args[1]) : 16);
|
||||
debugger->echoDisassemble(source, hex(args[0]), argc == 2 ? decimal(args[1]) : 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "export" && argc <= 1) {
|
||||
string filename = {debugger->sourceName(source), "-", string::datetime().transform(" :", "--"), ".bin"};
|
||||
string filename = {debugger->sourceName(source), "-", string::datetime().transform(" :", "--"), ".ram"};
|
||||
if(argc >= 1) filename = args[0];
|
||||
string pathname = {interface->pathname, "loki/", filename};
|
||||
debugger->memoryExport(source, pathname);
|
||||
@ -118,68 +131,65 @@ void Terminal::command(string t) {
|
||||
|
||||
if(s == "run.for" && argc == 1) {
|
||||
debugger->run();
|
||||
debugger->cpuRunFor = {true, (unsigned)decimal(args[0])};
|
||||
if(source == Debugger::Source::CPU) debugger->cpuRunFor = (unsigned)decimal(args[0]);
|
||||
if(source == Debugger::Source::SMP) debugger->smpRunFor = (unsigned)decimal(args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "run.to" && argc == 1) {
|
||||
debugger->run();
|
||||
debugger->cpuRunTo = {true, (unsigned)hex(args[0])};
|
||||
if(source == Debugger::Source::CPU) debugger->cpuRunTo = (unsigned)hex(args[0]);
|
||||
if(source == Debugger::Source::SMP) debugger->smpRunTo = (unsigned)hex(args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step" && argc == 0) {
|
||||
debugger->run();
|
||||
debugger->cpuStepFor = {true, 1u};
|
||||
if(source == Debugger::Source::CPU) debugger->cpuStepFor = 1u;
|
||||
if(source == Debugger::Source::SMP) debugger->smpStepFor = 1u;
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step.for" && argc == 1) {
|
||||
debugger->run();
|
||||
debugger->cpuStepFor = {true, (unsigned)decimal(args[0])};
|
||||
if(source == Debugger::Source::CPU) debugger->cpuStepFor = (unsigned)decimal(args[0]);
|
||||
if(source == Debugger::Source::SMP) debugger->smpStepFor = (unsigned)decimal(args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step.to" && argc == 1) {
|
||||
debugger->run();
|
||||
debugger->cpuStepTo = {true, (unsigned)hex(args[0])};
|
||||
if(source == Debugger::Source::CPU) debugger->cpuStepTo = (unsigned)hex(args[0]);
|
||||
if(source == Debugger::Source::SMP) debugger->smpStepTo = (unsigned)hex(args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.enable" && argc <= 1) {
|
||||
if(debugger->tracerFile.open() == false) {
|
||||
string filename = {"trace-", string::datetime().transform(" :", "--"), ".log"};
|
||||
if(argc >= 1) filename = args[0];
|
||||
string pathname = {interface->pathname, "loki/", filename};
|
||||
if(debugger->tracerFile.open(pathname, file::mode::write)) {
|
||||
echo("Tracer enabled\n");
|
||||
}
|
||||
}
|
||||
string filename = {debugger->sourceName(source), "-trace-", string::datetime().transform(" :", "--"), ".log"};
|
||||
if(argc >= 1) filename = args[0];
|
||||
string pathname = {interface->pathname, "loki/", filename};
|
||||
debugger->tracerEnable(source, pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.disable") {
|
||||
if(debugger->tracerFile.open() == true) {
|
||||
debugger->tracerFile.close();
|
||||
echo("Tracer disabled\n");
|
||||
}
|
||||
debugger->tracerDisable(source);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.mask" && argc == 1) {
|
||||
if(args[0] != "false") {
|
||||
debugger->tracerMask.resize(0x1000000);
|
||||
debugger->tracerMask.clear();
|
||||
echo("Tracer mask enabled\n");
|
||||
} else {
|
||||
debugger->tracerMask.reset();
|
||||
echo("Tracer mask disabled\n");
|
||||
}
|
||||
if(s == "tracer.mask.enable") {
|
||||
debugger->tracerMaskEnable(source);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.mask.disable") {
|
||||
debugger->tracerMaskDisable(source);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "usage.reset") {
|
||||
memset(debugger->usageCPU, 0, 0x1000000);
|
||||
if(source == Debugger::Source::CPU) memset(debugger->cpuUsage, 0x00, 0x1000000);
|
||||
if(source == Debugger::Source::APU) memset(debugger->apuUsage, 0x00, 0x10000);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user