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