From 05c63c3f1c5c184688a3f7fc8244d639c3acb95d Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Mon, 27 Apr 2015 19:29:50 -0500 Subject: [PATCH] Implement serialize_binary() and header writing. --- src/assembler.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/rom.c | 42 +----------------------------------------- src/rom.h | 5 +++++ src/util.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- src/util.h | 2 ++ 5 files changed, 109 insertions(+), 46 deletions(-) diff --git a/src/assembler.c b/src/assembler.c index ed1024a..91e226a 100644 --- a/src/assembler.c +++ b/src/assembler.c @@ -101,16 +101,65 @@ static ErrorInfo* resolve_symbols(AssemblerState *state) } /* + Write the ROM header to the binary. Header contents are explained in rom.c. +*/ +static void write_header(const ASMHeaderInfo *info, uint8_t *binary) +{ + uint8_t *header = binary + info->offset; + + // Bytes 0-7: magic string + memcpy(header, rom_header_magic, HEADER_MAGIC_LEN); + header += HEADER_MAGIC_LEN; + + // Bytes 8, 9: unused + *(header++) = 0x00; + *(header++) = 0x00; + + // Bytes A, B: checksum + if (info->checksum) { + uint16_t checksum = compute_checksum(binary, 0, info->rom_size); + *header = checksum & 0xFF; + *(header + 1) = checksum >> 8; + } else { + *header = *(header + 1) = 0x00; + } + header += 2; + + // Bytes C, D: product code (least significant two bytes) + *header = bcd_encode(info->product_code % 100); + *(header + 1) = bcd_encode((info->product_code / 100) % 100); + header += 2; + + // Byte E: product code (most significant nibble), version + *header = (info->product_code / 10000) << 4 | (info->version & 0x0F); + header++; + + // Byte F: region code, ROM size + *header = (info->region << 4) | (info->rom_size & 0x0F); +} + +/* Convert finalized ASMInstructions and ASMData into a binary data block. This function should never fail. */ -static void serialize_binary(AssemblerState *state, uint8_t *binary) +static void serialize_binary(const AssemblerState *state, uint8_t *binary) { - // TODO + memset(binary, 0xFF, state->rom_size); + + const ASMInstruction *inst = state->instructions; + while (inst) { + memcpy(binary + inst->loc.offset, inst->bytes, inst->loc.length); + inst = inst->next; + } + + const ASMData *data = state->data; + while (data) { + memcpy(binary + data->loc.offset, data->bytes, data->loc.length); + data = data->next; + } - for (size_t i = 0; i < state->rom_size; i++) - binary[i] = 'X'; + write_header(&state->header, binary); } /* diff --git a/src/rom.c b/src/rom.c index 2e9589c..c7d8667 100644 --- a/src/rom.c +++ b/src/rom.c @@ -14,11 +14,9 @@ #include "util.h" #define NUM_LOCATIONS 3 -#define MAGIC_LEN 8 #define SIZE_CODE_BUF 8 static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x3FF0, 0x1FF0}; -static const char header_magic[MAGIC_LEN + 1] = "TMR SEGA"; #ifdef DEBUG_MODE /* @@ -44,44 +42,6 @@ static void print_header(const uint8_t *header) } #endif -/* - Compute the correct ROM data checksum. - - If the summable region (as specified by the range parameter) is too large, - we'll compute the checksum over the default range (0x0000-0x7FF0), or the - largest possible range. -*/ -static uint16_t compute_checksum(const uint8_t *data, size_t size, uint8_t range) -{ - size_t low_region, high_region; - switch (range & 0xF) { - case 0xA: low_region = 0x1FEF; high_region = 0; break; - case 0xB: low_region = 0x3FEF; high_region = 0; break; - case 0xC: low_region = 0x7FEF; high_region = 0; break; - case 0xD: low_region = 0xBFEF; high_region = 0; break; - case 0xE: low_region = 0x7FEF; high_region = 0x0FFFF; break; - case 0xF: low_region = 0x7FEF; high_region = 0x1FFFF; break; - case 0x0: low_region = 0x7FEF; high_region = 0x3FFFF; break; - case 0x1: low_region = 0x7FEF; high_region = 0x7FFFF; break; - case 0x2: low_region = 0x7FEF; high_region = 0xFFFFF; break; - default: low_region = 0x7FEF; high_region = 0; break; - } - - if (low_region >= size) - low_region = (size >= 0x4000) ? 0x3FEF : 0x1FEF; - if (high_region >= size) - high_region = 0; - - uint16_t sum = 0; - for (size_t index = 0; index <= low_region; index++) - sum += data[index]; - if (high_region) { - for (size_t index = 0x08000; index <= high_region; index++) - sum += data[index]; - } - return sum; -} - #ifdef DEBUG_MODE /* DEBUG FUNCTION: Given a ROM size, return a pretty string. @@ -171,7 +131,7 @@ static bool find_and_read_header(ROM *rom) } DEBUG(" - trying location 0x%zX:", location) header = &rom->data[location]; - if (memcmp(header, header_magic, MAGIC_LEN)) { + if (memcmp(header, rom_header_magic, HEADER_MAGIC_LEN)) { DEBUG(" - magic not present") } else { diff --git a/src/rom.h b/src/rom.h index 4e4465d..15de218 100644 --- a/src/rom.h +++ b/src/rom.h @@ -9,7 +9,12 @@ #define ROM_SIZE_MIN (32 << 10) // 32 KB #define ROM_SIZE_MAX ( 1 << 20) // 1 MB +/* Header info */ + #define HEADER_SIZE 16 +#define HEADER_MAGIC_LEN 8 + +static const char rom_header_magic[HEADER_MAGIC_LEN + 1] = "TMR SEGA"; /* Error strings */ diff --git a/src/util.c b/src/util.c index 1be96bc..65c83af 100644 --- a/src/util.c +++ b/src/util.c @@ -20,7 +20,15 @@ #define NS_PER_SEC 1000000000 /* - Convert a BCD-encoded hexadecimal number to decimal. + Convert a decimal integer to BCD-encoded form. +*/ +uint8_t bcd_encode(uint8_t num) +{ + return ((num / 10) << 4) | (num % 10); +} + +/* + Convert a BCD-encoded integer to decimal. */ uint8_t bcd_decode(uint8_t num) { @@ -141,3 +149,42 @@ uint8_t size_bytes_to_code(size_t bytes) default: return INVALID_SIZE_CODE; } } + +/* + Compute a ROM data checksum. + + If the summable region (as specified by the range parameter) is too large + (as specified by the size parameter), we'll compute the checksum over the + default range (0x0000-0x7FF0), or the largest possible range. If size is + zero, we will compute it over the given range regardless. +*/ +uint16_t compute_checksum(const uint8_t *data, size_t size, uint8_t range) +{ + size_t low_region, high_region; + switch (range & 0xF) { + case 0xA: low_region = 0x1FEF; high_region = 0; break; + case 0xB: low_region = 0x3FEF; high_region = 0; break; + case 0xC: low_region = 0x7FEF; high_region = 0; break; + case 0xD: low_region = 0xBFEF; high_region = 0; break; + case 0xE: low_region = 0x7FEF; high_region = 0x0FFFF; break; + case 0xF: low_region = 0x7FEF; high_region = 0x1FFFF; break; + case 0x0: low_region = 0x7FEF; high_region = 0x3FFFF; break; + case 0x1: low_region = 0x7FEF; high_region = 0x7FFFF; break; + case 0x2: low_region = 0x7FEF; high_region = 0xFFFFF; break; + default: low_region = 0x7FEF; high_region = 0; break; + } + + if (size && low_region >= size) + low_region = (size >= 0x4000) ? 0x3FEF : 0x1FEF; + if (size && high_region >= size) + high_region = 0; + + uint16_t sum = 0; + for (size_t index = 0; index <= low_region; index++) + sum += data[index]; + if (high_region) { + for (size_t index = 0x08000; index <= high_region; index++) + sum += data[index]; + } + return sum; +} diff --git a/src/util.h b/src/util.h index 237ad21..a761602 100644 --- a/src/util.h +++ b/src/util.h @@ -9,9 +9,11 @@ /* Functions */ +uint8_t bcd_encode(uint8_t); uint8_t bcd_decode(uint8_t); uint64_t get_time_ns(); const char* region_code_to_string(uint8_t); uint8_t region_string_to_code(const char*); size_t size_code_to_bytes(uint8_t); uint8_t size_bytes_to_code(size_t); +uint16_t compute_checksum(const uint8_t*, size_t, uint8_t);