298 lines
9.4 KiB
C
298 lines
9.4 KiB
C
|
/* 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 */
|