@@ -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. | Convert finalized ASMInstructions and ASMData into a binary data block. | ||||
This function should never fail. | 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" | #include "util.h" | ||||
#define NUM_LOCATIONS 3 | #define NUM_LOCATIONS 3 | ||||
#define MAGIC_LEN 8 | |||||
#define SIZE_CODE_BUF 8 | #define SIZE_CODE_BUF 8 | ||||
static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x3FF0, 0x1FF0}; | static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x3FF0, 0x1FF0}; | ||||
static const char header_magic[MAGIC_LEN + 1] = "TMR SEGA"; | |||||
#ifdef DEBUG_MODE | #ifdef DEBUG_MODE | ||||
/* | /* | ||||
@@ -44,44 +42,6 @@ static void print_header(const uint8_t *header) | |||||
} | } | ||||
#endif | #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 | #ifdef DEBUG_MODE | ||||
/* | /* | ||||
DEBUG FUNCTION: Given a ROM size, return a pretty string. | 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) | DEBUG(" - trying location 0x%zX:", location) | ||||
header = &rom->data[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") | DEBUG(" - magic not present") | ||||
} | } | ||||
else { | else { | ||||
@@ -9,7 +9,12 @@ | |||||
#define ROM_SIZE_MIN (32 << 10) // 32 KB | #define ROM_SIZE_MIN (32 << 10) // 32 KB | ||||
#define ROM_SIZE_MAX ( 1 << 20) // 1 MB | #define ROM_SIZE_MAX ( 1 << 20) // 1 MB | ||||
/* Header info */ | |||||
#define HEADER_SIZE 16 | #define HEADER_SIZE 16 | ||||
#define HEADER_MAGIC_LEN 8 | |||||
static const char rom_header_magic[HEADER_MAGIC_LEN + 1] = "TMR SEGA"; | |||||
/* Error strings */ | /* Error strings */ | ||||
@@ -20,7 +20,15 @@ | |||||
#define NS_PER_SEC 1000000000 | #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) | 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; | 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 */ | /* Functions */ | ||||
uint8_t bcd_encode(uint8_t); | |||||
uint8_t bcd_decode(uint8_t); | uint8_t bcd_decode(uint8_t); | ||||
uint64_t get_time_ns(); | uint64_t get_time_ns(); | ||||
const char* region_code_to_string(uint8_t); | const char* region_code_to_string(uint8_t); | ||||
uint8_t region_string_to_code(const char*); | uint8_t region_string_to_code(const char*); | ||||
size_t size_code_to_bytes(uint8_t); | size_t size_code_to_bytes(uint8_t); | ||||
uint8_t size_bytes_to_code(size_t); | uint8_t size_bytes_to_code(size_t); | ||||
uint16_t compute_checksum(const uint8_t*, size_t, uint8_t); |