Hi, while toying with Bocfel 2.4 I added two extra /debug commands that I found really useful.
dump [address] [number_of_bytes]: outputs raw bytes from memory
overwrite [address] [values…]: writes successive bytes starting at [address]
I’ve been using overwrite to modify Z-machine opcodes at runtime, and dump to check the original data or confirm the overwrites. Both seem to work fine.
I couldn’t, for the life of me, find Bocfel’s GitHub repo, so I’ll just drop the diff for meta.cpp here. ![]()
--- "meta - original.cpp" 2025-08-26 01:58:09.000000000 +0200
+++ meta.cpp 2025-09-04 17:01:20.143515100 +0200
@@ -213,6 +213,111 @@
return true;
}
+
+static bool meta_debug_dump(const std::string &string)
+{
+ // Syntax: dump [address] [number_of_bytes]
+ std::istringstream ss(rtrim(string));
+ std::string addrstr, num_bytes_str;
+
+ if (!(ss >> addrstr >> num_bytes_str)) {
+ return false;
+ }
+
+ std::string extra;
+ if (ss >> extra) {
+ return false;
+ }
+
+ bool valid;
+ long addr_long = parse_address(addrstr, valid);
+ if (!valid) {
+ return false;
+ }
+
+ if (addr_long < 0 || static_cast<uint32_t>(addr_long) >= memory_size) {
+ screen_printf("[Address out of range: must be [0, 0x%lx]]\n", static_cast<unsigned long>(memory_size) - 1);
+ return true;
+ }
+
+ long num_bytes = parseint(num_bytes_str, 0, valid);
+ if (!valid || num_bytes <= 0) {
+ screen_puts("[Number of bytes must be a positive integer]");
+ return true;
+ }
+
+ uint32_t addr = static_cast<uint32_t>(addr_long);
+ if (static_cast<uint64_t>(addr) + static_cast<uint64_t>(num_bytes) > memory_size) {
+ num_bytes = memory_size - addr;
+ screen_printf("[Warning: range exceeds memory. Dumping %ld byte%s instead.]\n", num_bytes, num_bytes == 1 ? "" : "s");
+ }
+
+ for (long i = 0; i < num_bytes; i++) {
+ screen_printf("0x%02x%s", memory[addr + static_cast<uint32_t>(i)], (i == num_bytes - 1) ? "" : " ");
+ }
+ screen_puts("");
+ return true;
+}
+
+static bool meta_debug_overwrite(const std::string &string)
+{
+ // Syntax: overwrite [address] [values...]
+ // [address]: absolute (hex, optional 0x) or global variable Gxx
+ // [values]: one or more byte values (decimal, hex with 0x, or octal with leading 0)
+ std::istringstream ss(rtrim(string));
+ std::string addrstr;
+
+ if (!(ss >> addrstr)) {
+ return false;
+ }
+
+ bool valid;
+ long addr_long = parse_address(addrstr, valid);
+ if (!valid) {
+ return false;
+ }
+
+ if (addr_long < 0 || static_cast<uint32_t>(addr_long) >= memory_size) {
+ screen_printf("[Address out of range: must be [0, 0x%lx]]\n", static_cast<unsigned long>(memory_size) - 1);
+ return true;
+ }
+
+ std::vector<uint8_t> bytes;
+ for (std::string tok; ss >> tok; ) {
+ long v = parseint(tok, 0, valid);
+ if (!valid) {
+ return false;
+ }
+ if (v < 0 || v > 0xff) {
+ screen_puts("[Values must be in the range [0, 255]]");
+ return true;
+ }
+ bytes.push_back(static_cast<uint8_t>(v));
+ }
+
+ if (bytes.empty()) {
+ screen_puts("[No values]");
+ return true;
+ }
+
+ uint32_t addr = static_cast<uint32_t>(addr_long);
+ uint32_t end = addr + static_cast<uint32_t>(bytes.size());
+ if (end > memory_size) {
+ // Truncate to end of memory; this mirrors other debug commands' forgiving behavior
+ bytes.resize(memory_size - addr);
+ }
+
+ for (size_t i = 0; i < bytes.size(); i++) {
+ store_byte(addr + static_cast<uint32_t>(i), bytes[i]);
+ }
+
+ screen_printf("[Overwrote %lu byte%s starting at %s]\n",
+ static_cast<unsigned long>(bytes.size()),
+ bytes.size() == 1 ? "" : "s",
+ addrstring(static_cast<uint16_t>(addr)).c_str());
+
+ return true;
+}
static bool meta_debug_print(const std::string &string)
{
bool valid;
@@ -453,6 +558,9 @@
"scan [n]: update scan list with all words equal to [n]; if [n] starts with 0x it is hexadecimal, 0 it is octal, decimal otherwise\n"
"scan show: print all locations matching scan criteria\n"
"print [address]: print the word at address [address]\n"
+ "dump [address] [number_of_bytes]: dump raw bytes from memory\n"
+ "overwrite [address] [values...]: overwrite successive bytes starting at [address]\n"
+
#ifndef ZTERP_NO_CHEAT
"freeze [address] [value]: freeze the 16-bit [value] at [address]; [value] can be decimal, hexadecimal, or octal, with a leading 0x signifying hexadecimal and a leading 0 signifying octal\n"
"unfreeze [address]: unfreeze the value currently frozen at [address]\n"
@@ -485,6 +593,12 @@
} else if (cmd == "print") {
ok = meta_debug_print(args);
}
+ else if (cmd == "dump") {
+ ok = meta_debug_dump(args);
+ }
+ else if (cmd == "overwrite") {
+ ok = meta_debug_overwrite(args);
+ }
#ifndef ZTERP_NO_CHEAT
else if (cmd == "freeze") {
ok = meta_debug_freeze(args);
@@ -856,4 +970,4 @@
if (first_run) {
stash_register(std::make_unique<NotesStasher>());
}
-}
+}
\ No newline at end of file