#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 = "JTUBKAEL ", .peripherals = "J ", .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 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)"); } static int process_symbols(const char *progname, char *list, size_t size, const char *arg) { while (size-- && *arg) *list++ = *arg++; while (size--) *list++ = ' '; return *arg; } static int process_address(const char *progname, uint32_t *addr, const char *arg) { char *end; errno = 0; unsigned long val = strtoul(arg, &end, 16); if (end == arg || *end || errno || val > (uint32_t)-1) return 1; *addr = val; return 0; } #define FLAG_o (1U << 3) static int process_args(int argc, char **argv) { uint32_t seen = 0; int opt, fail = 0; extern char *optarg; extern int optind, optopt; static const char *optpat = "hi:o:p:r:m:s:"; while ((opt = getopt(argc, argv, optpat)) != -1) { uint32_t flag = opt == '?' ? 0 : 1U << strchr(optpat, opt) - optpat; if (seen & flag) { fprintf(stderr, "%s: Duplicate option -%c\n", argv[0], opt); fail = 1; continue; } seen |= flag; switch (opt) { case 'h': usage(argv[0]); return 1; case 'i': ipfile = optarg; break; case 'o': outfile = optarg; break; case 'm': if (process_address(argv[0], &sysid.stack_master, optarg)) { fprintf(stderr, "%s: Invalid master stack\n", argv[0]); fail = 1; } break; case 's': if (process_address(argv[0], &sysid.stack_slave, optarg)) { fprintf(stderr, "%s: Invalid slave stack\n", argv[0]); fail = 1; } break; case 'p': if (process_symbols(argv[0], sysid.peripherals, sizeof sysid.peripherals, optarg)) { fprintf(stderr, "%s: Too many peripherals specified (max 16)\n", argv[0]); fail = 1; } break; case 'r': if (process_symbols(argv[0], sysid.regions, sizeof sysid.regions, optarg)) { fprintf(stderr, "%s: Too many regions specified (max 10)\n", argv[0]); fail = 1; } break; default: fail = 1; } } if (!(seen & FLAG_o)) { fprintf(stderr, "%s: Missing required option -o\n", argv[0]); fail = 1; } if (argv[optind]) { fprintf(stderr, "%s: Unknown option '%s'\n", argv[0], argv[optind]); fail = 1; } if (fail) fprintf(stderr, "\nUse -h option for help on correct usage.\n"); return fail; } int main(int argc, char **argv) { FILE *outfp = NULL; char *ipout = ipbuf; size_t ipsize; if (process_args(argc, argv)) { return 1; } 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; }