diff options
Diffstat (limited to 'memory.c')
-rw-r--r-- | memory.c | 190 |
1 files changed, 190 insertions, 0 deletions
@@ -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)); + } +} |