| @@ -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); | |||||