/* 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 */