diff --git a/util/isx.c b/util/isx.c new file mode 100644 index 0000000..7d31c3d --- /dev/null +++ b/util/isx.c @@ -0,0 +1,297 @@ +/* This file is included into vbu.c and cannot be compiled on its own. */ +#ifdef VBUAPI + + + +/********************************* Constants *********************************/ + +/* Range types */ +/* +0x41 ('A') 8-bit integer (.db), string +0x42 ('B') 8-bit integer (.db), numeric +0x43 ('C') Compiled C code +0x44 ('D') 16-bit integer (.dd, high byte first) +0x45 ('E') 65C816 code (8-bit accumulator, 8-bit index) +0x46 ('F') 65C816 code (8-bit accumulator, 16-bit index) +0x47 ('G') 65C816 code (16-bit accumulator, 8-bit index) +0x48 ('H') 65C816 code (16-bit accumulator, 16-bit index) +0x4C ('L') 32-bit integer (.dl SNES, .dw Virtual Boy, low byte first) +0x53 ('S') Size reservation, as in .bss allocation (.ds) +0x57 ('W') 16-bit integer (.dw SNES, .dh Virtual Boy, low byte first) +*/ + + + +/***************************** Module Functions ******************************/ + +/* Skip ISX record type 0x01 */ +static size_t isx01(uint8_t *bytes, size_t length, size_t src) { + /* + u8 Type =0x01 + u8 Bank Cartridge bank number + u8 BankHigh Only if Bank >= 0x80 + u16 Address CPU bus address + u16 DataLength Number of bytes in Data + u8[] Data Code bytes + */ + + /* Bank field is not present */ + if (src + 2 > length) + return (size_t) -1; + + /* Advance to DataLength field */ + src += bytes[src + 1] >= 0x80 ? 5 : 4; + + /* DataLength field is not present */ + if (src + 2 > length) + return (size_t) -1; + + /* Advance past Data field */ + return src + 2 + ((uint16_t) bytes[src + 1] << 8 | bytes[src]); +} + +/* Decode ISX record type 0x11 */ +static size_t isx11(uint8_t *bytes, size_t length, size_t src, + uint32_t *address, uint32_t *size) { + /* + u8 Type =0x11 + u32 Address CPU bus address + u32 DataLength Number of bytes in Data + u8[] Data Code bytes + */ + + /* DataLength field is not present */ + if (src + 9 > length) + return (size_t) -1; + + /* Parse Address and DataLength fields */ + *address = (uint32_t) bytes[src+4] << 24 | (uint32_t) bytes[src+3] << 16 | + (uint32_t) bytes[src+2] << 8 | bytes[src+1]; + *size = (uint32_t) bytes[src+8] << 24 | (uint32_t) bytes[src+7] << 16 | + (uint32_t) bytes[src+6] << 8 | bytes[src+5]; + + /* Advance past Data field */ + return src + 9 + *size; +} + +/* Skip ISX range records */ +static size_t isxRange(uint8_t *bytes,size_t length,size_t src,size_t size) { + /* + u8 Type =0x03 (SNES) or 0x13 (Virtual Boy) + u16 Count Number of ranges + Range[] Ranges + === SNES fields === + u8 Bank Cartridge bank number + u16 StartAddress Lowest CPU bus address + u16 EndAddress Highest CPU bus address + u8 RangeType See range types + === Virtual Boy fields === + u32 StartAddress Lowest CPU bus address + u32 EndAddress Highest CPU bus address + u8 RangeType See range types + */ + + /* Count field is not present */ + if (src + 3 > length) + return (size_t) -1; + + /* Advance past Ranges field */ + return src + 3 + size * ((uint16_t) bytes[src + 2] << 8 | bytes[src + 1]); +} + +/* Skip ISX symbol record */ +static size_t isxSymbol(uint8_t *bytes,size_t length,size_t src,size_t size) { + size_t count; /* Number of symbols */ + size_t x; /* Iterator */ + + /* + u8 Type =0x04 (SNES) or 0x14 (Virtual Boy) + u16 Count Number of symbols + Symbol[] Symbols + u8 NameLength Number of bytes in Name + u8[] Name Symbol name + === SNES fields === + u8 Flags Undocumented + u8 Bank Cartridge bank number + u16 Address CPU bus address + === Virtual Boy fields === + u16 Flags Undocumented + u32 Address CPU bus address + */ + + /* Count field is not present */ + if (src + 3 > length) + return (size_t) -1; + + /* Working variables */ + count = (uint16_t) bytes[src + 2] << 8 | bytes[src + 1]; + src += 3; + + /* Process all symbols */ + for (x = 0; x < count; x++) { + + /* NameLength field is not present */ + if (src > length) + return (size_t) -1; + + /* Advance past remainder of symbol */ + src += size + bytes[src]; + + /* Remainder of symbol is not present */ + if (src > length) + return (size_t) -1; + } + + return src; +} + +/* Skip ISX system record */ +static size_t isxSystem(uint8_t *bytes, size_t length, size_t src) { + /* + u8 Type =0x20, 0x21 or 0x22 + u32 DataLength Number of bytes in Data + u8[] Data Record data + */ + + /* DataLength field is not present */ + if (src + 5 > length) + return (size_t) -1; + + /* Advance past Data field */ + return src + 5 + ( + (uint32_t) bytes[src + 4] << 24 | (uint32_t) bytes[src + 3] << 16 | + (uint32_t) bytes[src + 2] << 8 | bytes[src + 1] + ); +} + + + +/***************************** Library Functions *****************************/ + +/* Decode an ISX debugger file to a Virtual Boy ROM */ +static void* isxFrom(uint8_t *bytes, size_t length, size_t *romLength) { + uint32_t addr; /* Code record base address */ + uint32_t highMin; /* Lowest address in upper half of ROM */ + uint32_t lowMax; /* Highest address in lower half of ROM */ + uint8_t *rom; /* Output ROM */ + uint32_t size; /* Code record size in bytes */ + size_t src; /* Input position */ + size_t start; /* Offset of first record */ + + /* Error checking */ + if (length < 3) + return NULL; + + /* Working variables */ + highMin = 0xFFFFFF; + lowMax = 0x000000; + size = 0; + start = strncmp((char *) bytes, "ISX", 3) ? 0 : 32; + + /* First pass: determine the size of the ROM */ + for (src = start; src < length;) { + switch (bytes[src]) { + + case 0x11: /* Code (Virtual Boy) */ + + /* Decode record fields */ + src = isx11(bytes, length, src, &addr, &size); + if (src > length) + return NULL; + + /* Validate address and data size */ + addr &= 0x07FFFFFF; + if ( + addr < 0x07000000 || + 0x08000000 - addr < size + ) return NULL; + addr &= 0x00FFFFFF; + + /* Requires 16 MiB */ + if (addr < 0x800000 && addr + size > 0x800000) { + highMin = 0x800000; + lowMax = 0x7FFFFF; + } + + /* Map from top of ROM space */ + else if (addr & 0x800000) { + if (addr < highMin) + highMin = addr; + } + + /* Map from bottom of ROM space */ + else { + addr += size - 1; + if (addr > lowMax) + lowMax = addr; + } + + break; + + case 0x01: /* Code (SNES) */ + src = isx01 (bytes, length, src ); break; + case 0x03: /* Range (SNES) */ + src = isxRange (bytes, length, src, 6); break; + case 0x04: /* Symbol (SNES) */ + src = isxSymbol(bytes, length, src, 5); break; + case 0x13: /* Range (Virtual Boy) */ + src = isxRange (bytes, length, src, 9); break; + case 0x14: /* Symbol (Virtual Boy) */ + src = isxSymbol(bytes, length, src, 7); break; + case 0x20: /* System (undocumented) */ + case 0x21: /* System (undocumented) */ + case 0x22: /* System (undocumented) */ + src = isxSystem(bytes, length, src ); break; + default: /* Invalid record type */ + return NULL; + } + + /* Record contains invalid data */ + if (src > length) + return NULL; + } + + /* Produce a ROM buffer of minimal size to fit all the code records */ + for ( + src = 1; + src < lowMax + 0x1000001 - highMin; + src <<= 1 + ); + rom = VBU_REALLOC(NULL, src); + if (rom == NULL) + return NULL; + *romLength = src; + memset(rom, 0x00, src); + + /* Second pass: fill the ROM buffer with the code records */ + for (src = start; src < length;) { + switch (bytes[src]) { + + case 0x11: /* Code (Virtual Boy) */ + src = isx11(bytes, length, src, &addr, &size); + memcpy(&rom[addr & (*romLength-1)], &bytes[src - size], size); + break; + + case 0x01: /* Code (SNES) */ + src = isx01 (bytes, length, src ); break; + case 0x03: /* Range (SNES) */ + src = isxRange (bytes, length, src, 9); break; + case 0x04: /* Symbol (SNES) */ + src = isxSymbol(bytes, length, src, 5); break; + case 0x13: /* Range (Virtual Boy) */ + src = isxRange (bytes, length, src, 12); break; + case 0x14: /* Symbol (Virtual Boy) */ + src = isxSymbol(bytes, length, src, 7); break; + case 0x20: /* System (undocumented) */ + case 0x21: /* System (undocumented) */ + case 0x22: /* System (undocumented) */ + src = isxSystem(bytes, length, src ); break; + } + } + + return rom; +} + + + +#endif /* VBUAPI */