summaryrefslogtreecommitdiff
path: root/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'memory.c')
-rw-r--r--memory.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/memory.c b/memory.c
index 309d1ae..8513f21 100644
--- a/memory.c
+++ b/memory.c
@@ -102,3 +102,193 @@ static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes
}
return idx;
}
+
+typedef struct {
+ FILE *fp;
+ Address curr_addr;
+} MemfileWriter;
+static char const MEMFILE_IDENT[4] = {'\xff', 'M', 'E', 'M'};
+
+static bool memfile_writer_open(State *state, MemfileWriter *writer, char const *filename) {
+ memset(writer, 0, sizeof *writer);
+ writer->fp = fopen(filename, "wb");
+ if (writer->fp) {
+ fwrite(MEMFILE_IDENT, 1, sizeof MEMFILE_IDENT, writer->fp);
+ return true;
+ } else {
+ display_error(state, "Couldn't open %s: %s.", filename, strerror(errno));
+ return false;
+ }
+}
+
+static void memfile_writer_close(MemfileWriter *writer) {
+ if (writer->fp) fclose(writer->fp);
+ memset(writer, 0, sizeof *writer);
+}
+
+
+// write up to 64K of consecutive memory.
+static void memfile_write_bytes(MemfileWriter *writer, Address addr, uint8_t const *data, size_t nbytes) {
+ Address addr_increment = addr - writer->curr_addr;
+ // set address
+ if (addr_increment < 64) {
+ putc((int)addr_increment, writer->fp);
+ } else if (addr_increment < 8192) {
+ putc(0x40 | (int)(addr_increment & 0x1f), writer->fp);
+ putc((int)(addr_increment >> 5), writer->fp);
+ } else {
+ putc(0x60, writer->fp);
+ fwrite(&addr, sizeof addr, 1, writer->fp);
+ }
+
+ if (nbytes < 64) {
+ putc(0x80 | (int)nbytes, writer->fp);
+ } else if (nbytes < 65536) {
+ putc(0xC0, writer->fp);
+ fwrite(&nbytes, 2, 1, writer->fp);
+ } else {
+ putc(0xE0, writer->fp);
+ fwrite(&nbytes, sizeof nbytes, 1, writer->fp);
+ }
+ fwrite(data, 1, nbytes, writer->fp);
+
+ writer->curr_addr = addr + nbytes;
+}
+
+static void memfile_write_byte(MemfileWriter *writer, Address addr, uint8_t byte) {
+ memfile_write_bytes(writer, addr, &byte, 1);
+}
+
+static void memfile_write_all(State *state, char const *filename) {
+ if (!state->pid) return;
+ MemfileWriter writer = {0};
+ if (memfile_writer_open(state, &writer, filename)) {
+ int reader = memory_reader_open(state);
+ if (reader) {
+ for (unsigned m = 0; m < state->nmaps; ++m) {
+ Map *map = &state->maps[m];
+ uint8_t chunk[4096] = {0}; // page size is probably a multiple of 4096.
+ for (Address offset = 0; offset < map->size; offset += sizeof chunk) {
+ Address addr = map->lo + offset;
+ memory_read_bytes(reader, addr, chunk, sizeof chunk);
+ memfile_write_bytes(&writer, addr, chunk, sizeof chunk);
+ }
+ }
+ memory_reader_close(state, reader);
+ }
+ memfile_writer_close(&writer);
+ }
+}
+
+static void memfile_write_candidates(State *state, char const *filename) {
+ if (!state->pid) return;
+ size_t item_size = data_type_size(state->data_type);
+ MemfileWriter writer = {0};
+ if (memfile_writer_open(state, &writer, filename)) {
+ int reader = memory_reader_open(state);
+ if (reader) {
+ Address bitset_index = 0;
+ uint64_t *search_candidates = state->search_candidates;
+ for (unsigned m = 0; m < state->nmaps; ++m) {
+ Map *map = &state->maps[m];
+ Address n_items = map->size / item_size;
+ for (Address i = 0; i < n_items; ) {
+ if (bitset_index % 64 == 0 && search_candidates[bitset_index / 64] == 0) {
+ // no candidates here
+ i += 64;
+ bitset_index += 64;
+ } else {
+ if (search_candidates[bitset_index / 64] & MASK64(bitset_index % 64)) {
+ // a candidate!
+ Address addr = map->lo + i * item_size;
+ uint64_t value = 0;
+ memory_read_bytes(reader, addr, (uint8_t *)&value, item_size);
+ memfile_write_bytes(&writer, addr, (uint8_t const *)&value, item_size);
+ }
+ ++i;
+ ++bitset_index;
+ }
+ }
+ }
+ memory_reader_close(state, reader);
+ }
+ memfile_writer_close(&writer);
+ }
+}
+
+static void memfile_load(State *state, char const *filename) {
+ if (!state->pid) return;
+ FILE *fp = fopen(filename, "rb");
+ if (fp) {
+ char ident[sizeof MEMFILE_IDENT] = {0};
+ fread(ident, sizeof ident, 1, fp);
+ if (memcmp(ident, MEMFILE_IDENT, sizeof MEMFILE_IDENT) != 0) {
+ display_error(state, "%s is not a memory file.", filename);
+ } else {
+ int writer = memory_writer_open(state);
+ if (writer) {
+ Address addr = 0;
+
+ int first_byte;
+ while ((first_byte = getc(fp)) != EOF) {
+ uint64_t val = 0;
+ size_t nbytes = 0;
+ switch (first_byte & 0xE0) {
+ case 0x00:
+ case 0x20:
+ // 6 bits address increment
+ addr += first_byte & 0x3F;
+ break;
+ case 0x40:
+ // 5 bits + 1 byte address increment
+ addr += first_byte & 0x1F;
+ addr += (unsigned)getc(fp) << 5;
+ break;
+ case 0x60:
+ // constant 4/8-byte address
+ if (first_byte != 0x60)
+ goto invalid;
+ fread(&val, sizeof(Address), 1, fp);
+ addr = val;
+ break;
+ case 0x80:
+ case 0xA0:
+ // 6 bit length; data
+ nbytes = first_byte & 0x3F;
+ goto read_data;
+ case 0xC0:
+ if (first_byte != 0xC0) goto invalid;
+ // 2 bytes length; data
+ fread(&nbytes, 2, 1, fp);
+ goto read_data;
+ case 0xE0:
+ if (first_byte != 0xE0) goto invalid;
+ // 4/8 bytes length; data
+ fread(&nbytes, sizeof(size_t), 1, fp);
+ goto read_data;
+ read_data: {
+ uint8_t chunk[4096] = {0};
+ size_t bytes_left = nbytes;
+ while (bytes_left > 0) {
+ size_t chunk_len = bytes_left;
+ if (chunk_len > sizeof chunk) chunk_len = sizeof chunk;
+ fread(chunk, 1, chunk_len, fp);
+ memory_write_bytes(writer, addr, chunk, chunk_len);
+ addr += chunk_len;
+ bytes_left -= chunk_len;
+ }
+ } break;
+ invalid:
+ display_error(state, "%s is an invalid memory file.", filename);
+ goto eof;
+ }
+ }
+ eof:
+ memory_writer_close(state, writer);
+ }
+ }
+ fclose(fp);
+ } else {
+ display_error(state, "Couldn't open %s: %s.", filename, strerror(errno));
+ }
+}