#include #include #include #include #include #include #include "bootinfo.h" #include "symbols.h" static struct systemid sysid = { .magic = "SEGA SEGASATURN ", .maker = "SEGA TP T-000 ", .product = "T-000000G ", .version = "V0.001", .reldate = "20000101", .device = "CD-1/1 ", .regions = " ", .peripherals = " ", .padding1 = " ", .title = "TEST TITLE ", .bootsize = 0xe00, .stack_master = 0, .stack_slave = 0, .load_addr = 0x06004000, .load_size = 0, }; static char ipbuf[0x7200]; static char *ipfile, *outfile; static void serialize_region_code(char *out, const struct symbolname *region) { static const char defcode[32] = "\xa0\x0e\x00\x09" "For "; size_t namelen = region ? strlen(region->name) : 0; memcpy(out, defcode, sizeof defcode); if (namelen) memcpy(out+8, region->name, namelen); out[8+namelen] = '.'; } #define WRITE(f,p,s) fwrite(p,s,1,f) #define WRITE32(f,v) fwrite((char[]){(v)>>24,(v)>>16,(v)>>8,(v)},4,1,f) static int write_output(FILE *fp) { extern const unsigned char securitycode[]; extern const size_t securitycode_size; /* system id - 0x100 bytes */ WRITE (fp, &sysid, offsetof(struct systemid, bootsize)); WRITE32(fp, sysid.bootsize); WRITE32(fp, 0); /* reserved bytes */ WRITE32(fp, sysid.stack_master); WRITE32(fp, sysid.stack_slave); WRITE32(fp, sysid.load_addr); WRITE32(fp, sysid.load_size); WRITE32(fp, 0); /* reserved bytes */ WRITE32(fp, 0); /* reserved bytes */ /* security code */ WRITE(fp, securitycode, securitycode_size); /* initial program */ WRITE(fp, ipbuf, sysid.bootsize - 0xe00); return ferror(fp) ? -1 : 0; } static size_t default_ip(char *out) { size_t size = 0; out[size++] = 0xd0; /* mov.l addr, r0 */ out[size++] = 0x01; out[size++] = 0x40; /* jmp @r0 */ out[size++] = 0x2b; out[size++] = 0x00; /* nop */ out[size++] = 0x09; out[size++] = 0x00; /* nop */ out[size++] = 0x09; out[size++] = sysid.load_addr >> 24 & 0xff; /* addr: .long */ out[size++] = sysid.load_addr >> 16 & 0xff; out[size++] = sysid.load_addr >> 8 & 0xff; out[size++] = sysid.load_addr >> 0 & 0xff; return size; } static size_t load_ip(char *filename, char *out, size_t maxsize) { FILE *fp; size_t size; const char *errprefix = "Error loading initial program"; if (!(fp = fopen(filename, "rb"))) { goto fail_perror; } if ((size = fread(out, 1, maxsize, fp)) < maxsize) { /* read fewer than requested amount. determine if we hit error or eof */ if (ferror(fp)) { goto fail_perror; } } else { /* successfully read the requested amount. verify we reached eof */ if (fgetc(fp) != EOF) { fprintf(stderr, "%s: exceeds maximum size\n", errprefix); goto fail; } } fclose(fp); return size; fail_perror: perror(errprefix); fail: if (fp) fclose(fp); return -1; } static int append_symbol(char *list, size_t size, char symbol) { for (size_t i = 0; i < size; i++) { if (list[i] == ' ') list[i] = symbol; if (list[i] == symbol) return 0; } return -1; } static void print_symbols(FILE *fp, int width, const struct symbolname *symbols) { for (; symbols->symbol; symbols++) { fprintf(fp, "\t%*s%c: %s\n", width+4, "", symbols->symbol, symbols->name); } } static void usage(const char *progname) { const int width = 20; fprintf(stderr, "usage: %s -h\n", progname); fprintf(stderr, " %s -o[iprms]\n\n", progname); fprintf(stderr, "\t%-*s%s\n", width, "-h", "Show this help text"); fprintf(stderr, "\t%-*s%s\n", width, "-o output", "Output file"); fprintf(stderr, "\t%-*s%s\n", width, "-i ip.bin", "Initial program code file"); fprintf(stderr, "\t%-*s%s\n", width, "-p peripherals", "Supported peripherals (default: control pad):"); print_symbols(stderr, width, peripheraldefs); fprintf(stderr, "\t%-*s%s\n", width, "-r regions", "Geographical regions (default: all):"); print_symbols(stderr, width, regiondefs); fprintf(stderr, "\t%-*s%s\n", width, "-m master_stack", "Master stack address (default 0x06002000)"); fprintf(stderr, "\t%-*s%s\n", width, "-s slave_stack", "Slave stack address (default 0x06001000)"); } #define ERR_UNKNOWN_OPTION 0 #define ERR_MISSING_OPTION 1 #define ERR_DUPLICATE_INPUT 2 #define ERR_DUPLICATE_OUTPUT 3 #define ERR_REGION_OVERFLOW 4 #define ERR_PERIPHERAL_OVERFLOW 5 #define ERR_DUPLICATE_MASTER 6 #define ERR_DUPLICATE_SLAVE 7 #define ERR_INVALID_MASTER 8 #define ERR_INVALID_SLAVE 9 static const char *errmsgs[] = { "Unknown extra argument", "Missing required option", "Duplicate -i parameter", "Duplicate -o parameter", "Too many regions specified (max 10)", "Too many peripherals specified (max 16)", "Duplicate -m parameter", "Duplicate -s parameter", "Invalid master stack address", "Invalid slave stack address", }; static int show_arg_error(const char *progname, int prev_errors, int error) { if (!(prev_errors & 1 << error)) fprintf(stderr, "%s: %s\n", progname, errmsgs[error]); return prev_errors | 1 << error; } static void process_symbols(const char *progname, char *list, size_t size, const char *arg, int *errors, int overflow) { for (; *arg; arg++) { if (append_symbol(list, size, *arg)) { *errors = show_arg_error(progname, *errors, overflow); return; } } } static int process_address(const char *progname, uint32_t *addr, const char *arg, int *errors, int bad_addr) { char *end; errno = 0; unsigned long val = strtoul(arg, &end, 16); if (end == arg || *end || errno || val > (uint32_t)-1) { *errors = show_arg_error(progname, *errors, bad_addr); return 0; } *addr = val; return 1; } static int process_args(int argc, char **argv) { int errors = 0, fail = 0, help = 0; int opt; int have_master = 0; int have_slave = 0; extern char *optarg; extern int optind, optopt; while ((opt = getopt(argc, argv, "hi:o:p:r:m:s:")) != -1) { switch (opt) { case 'h': help = 1; break; case 'i': if (ipfile) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_INPUT); else ipfile = optarg; break; case 'o': if (outfile) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_OUTPUT); else outfile = optarg; break; case 'm': if (have_master) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_MASTER); else have_master = process_address(argv[0], &sysid.stack_master, optarg, &errors, ERR_INVALID_MASTER); break; case 's': if (have_slave) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_SLAVE); else have_slave = process_address(argv[0], &sysid.stack_slave, optarg, &errors, ERR_INVALID_SLAVE); break; case 'p': process_symbols(argv[0], sysid.peripherals, sizeof sysid.peripherals, optarg, &errors, ERR_PERIPHERAL_OVERFLOW); break; case 'r': process_symbols(argv[0], sysid.regions, sizeof sysid.regions, optarg, &errors, ERR_REGION_OVERFLOW); break; default: fail = 1; } } if (help) { usage(argv[0]); } else if (!fail) { if (argv[optind]) errors = show_arg_error(argv[0], errors, ERR_UNKNOWN_OPTION); if (!outfile) errors = show_arg_error(argv[0], errors, ERR_MISSING_OPTION); fail = errors; } if (fail) { fprintf(stderr, "\nUse -h option for help on correct usage.\n"); } return fail | help; } int main(int argc, char **argv) { FILE *outfp = NULL; char *ipout = ipbuf; size_t ipsize; if (process_args(argc, argv)) { return 1; } if (sysid.regions[0] == ' ') strcpy(sysid.regions, "JTUBKAEL"); if (sysid.peripherals[0] == ' ') sysid.peripherals[0] = 'J'; for (int i = 0; i < 8; i++) { serialize_region_code(ipout, find_symbol(regiondefs, sysid.regions[i])); ipout += 32; sysid.bootsize += 32; } ipsize = ipfile ? load_ip(ipfile, ipout, sizeof ipbuf - (ipout - ipbuf)) : default_ip(ipout); if (ipsize == -1) goto fail; sysid.bootsize += ipsize; if (!(outfp = fopen(outfile, "wb"))) { perror("Error opening output file"); goto fail; } if (write_output(outfp)) { perror("Error writing output"); goto fail; } fclose(outfp); return 0; fail: if (outfp) fclose(outfp); return 1; }