shrooms-vb-core/util/isx.c

298 lines
9.4 KiB
C
Raw Permalink Normal View History

2025-02-18 21:13:18 +00:00
/* 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 */