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:
Tim Allen 2014-02-09 16:59:46 +11:00
parent 423a6c6bf8
commit 3016e595f0
44 changed files with 504 additions and 774 deletions

View File

@ -6,8 +6,8 @@ gb := gb
gba := gba
profile := accuracy
target := higan
# target := loki
# target := higan
target := loki
ifeq ($(target),loki)
options += debugger

View File

@ -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/";

View File

@ -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() {}
};
}

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

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

View File

@ -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

View File

@ -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
View 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

View File

@ -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>

View File

@ -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) {

View File

@ -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();

View File

@ -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 = " ");

View File

@ -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); }
}

View File

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

View File

@ -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 {

View File

@ -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); }
}

View File

@ -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

View File

@ -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;

View File

@ -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() {

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);

View File

@ -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!

View File

@ -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),

View File

@ -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) };

View File

@ -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);

View File

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

View File

@ -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;

View File

@ -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()])();
}

View File

@ -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();

View File

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

View File

@ -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(" ", ""));

View File

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

View File

@ -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;

View File

@ -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");
}

View File

@ -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;

View File

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