/* :ts=4 mkkickwork - create A1000 kickstart disk with bootable OFS filesystem Copyright © 2007-2009 Harry Sintonen This source code is provided for reference purposes. If you wish to reuse parts of it, please contact the author for permission. */ #include #include #include #include #include #include #include #include #ifdef AMIGA const long __stack = 8192; #endif #ifdef __SASC extern int getopt(int argc, char * const argv[], char const *opts); extern int optind; extern char *optarg; #endif #define ROMSIZE (256 * 1024) #define ROMEND 0x01000000 #define ROMSTART (ROMEND - ROMSIZE) #define ROMSUMOFFSET -0x18 static __inline u_int32_t rl(const u_int8_t *p) { return (u_int32_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } static __inline void wl(u_int8_t *p, u_int32_t val) { p[0] = val >> 24; p[1] = val >> 16; p[2] = val >> 8; p[3] = val; } static __inline int romptr(u_int32_t p) { return p >= ROMSTART && p < ROMEND; } static u_int32_t blksum(void *p, size_t size) { u_int8_t *ptr = p; u_int32_t sum = (u_int32_t) -rl(ptr + 20); int i; for (i = 0; i < size; i += 4) { sum += rl(ptr + i); } return (u_int32_t) -sum; } static u_int32_t wrapsum(void *p, size_t size) { u_int8_t *ptr = p; u_int32_t nsum, sum = 0; int i; for (i = 0; i < size; i += 4) { nsum = sum + rl(ptr + i); sum = nsum < sum ? nsum + 1 : nsum; } return (u_int32_t) ~sum; } static void freeblk(void *bitmap, int blk) { u_int8_t *ptr; blk -= 2; /* bootblocks are not part of the bitmap */ ptr = (u_int8_t *) bitmap + blk / 32 * 4; wl(ptr, rl(ptr) | (1 << (blk & 31))); } struct datestamp { u_int32_t days; u_int32_t mins; u_int32_t ticks; }; #define SECONDSPERDAY (24 * 60 * 60) /* number of seconds between Jan 1 1970 and Jan 1 1978 */ #define EPOCHDELTA (2922 * SECONDSPERDAY) static void mkdatestamp(struct datestamp *ds, time_t time) { if (time >= EPOCHDELTA) { time -= EPOCHDELTA; ds->days = time / SECONDSPERDAY; ds->mins = (time % SECONDSPERDAY) / 60; ds->ticks = (time % 60) * 50; } else { ds->days = 0; ds->mins = 0; ds->ticks = 0; } } /* Source to this patch is in tdboot.asm */ static const u_int8_t patch[] = { 0x2F,0x09,0x4E,0xB9, 0x00,0xFC,0x00,0x00, 0x22,0x5F,0x43,0xE9, 0x00,0x1F,0x4A,0x19, 0x66,0x2E,0x20,0x11, 0x72,0xCE,0x50,0x89, 0xD2,0x80,0x20,0x59, 0xD0,0x91,0x0C,0x80, 0x00,0x00,0x04,0x00, 0x62,0x1A,0x0C,0x90, 0x4B,0x49,0x43,0x4B, 0x66,0x12,0x43,0xFA, 0x00,0x12,0x70,0x30, 0x10,0xD9,0x51,0xC8, 0xFF,0xFC,0x42,0x18, 0x51,0xC9,0xFF,0xFC, 0x4E,0x75, 0x44,0x4F,0x53,0x00, 0xC0,0x20,0x0F,0x19, 0x00,0x00,0x03,0x70, 0x43,0xFA,0x00,0x18, 0x4E,0xAE,0xFF,0xA0, 0x4A,0x80,0x67,0x0A, 0x20,0x40,0x20,0x68, 0x00,0x16,0x70,0x00, 0x4E,0x75,0x70,0xFF, 0x60,0xFA,0x64,0x6F, 0x73,0x2E,0x6C,0x69, 0x62,0x72,0x61,0x72, 0x79 }; /* Why include 2nd copy when we already have it inside the patch? */ const int bootblocksize = 49; const u_int8_t * const bootblock = &patch[sizeof(patch) - 49]; int usage(const char *progname) { fprintf(stderr, "usage: %s [-b] [-k] [-n volname] [-v[v]] ksrom outfile\n" "-b create DOS bootable floppy rather than A1000 kickstart disk\n" "-k output patched kickstart ROM rather than ADF\n" "-n specify volumename [default: Kickwork]\n" "-v verbose output, specify multiple times for more verbosity\n" "ksrom kickstart ROM file, only KS 1.x are supported for now\n" "outfile output file, either ADF or ROM depending on -k\n" ,progname); return EXIT_FAILURE; } int main(int argc, char **argv) { int rc = EXIT_FAILURE; int opt; int bootable = 0; int kickromonly = 0; int verbose = 0; const char *volname = "Kickwork"; int ok = 0; int fd; void *rom = 0; while ((opt = getopt(argc, argv, "bkn:v")) != -1) { switch (opt) { case 'b': bootable = 1; break; case 'k': kickromonly = 1; break; case 'n': if (strlen(optarg) > 30 || strpbrk(optarg, ":/#?~()[]|%*")) { fprintf(stderr, "illegal volumename \"%s\" ignored\n", optarg); } else { volname = optarg; } break; case 'v': verbose++; break; default: return usage(argv[0]); } } if (optind + 2 > argc) { fprintf(stderr, "required argument missing\n"); return usage(argv[0]); } if (verbose) { fprintf(stderr, "Reading the Kickstart ROM from \"%s\"...\n", argv[optind]); } fd = open(argv[optind], O_RDONLY); if (fd != -1) { struct stat st; #ifdef __SASC /* SAS/C is lame: fstat() only works with KS 2.0 and later */ if (stat(argv[optind], &st) != -1) #else if (fstat(fd, &st) != -1) #endif { if (st.st_size == ROMSIZE) { rom = malloc(ROMSIZE); if (rom) { if (read(fd, rom, ROMSIZE) == ROMSIZE) { ok = 1; } else perror("read"); } else perror("malloc"); } else fprintf(stderr, "rom size must be %u bytes\n", ROMSIZE); } #ifdef __SASC else perror("stat"); #else else perror("fstat"); #endif close(fd); } else perror("open for reading"); if (ok) { int i; void *newtdread = 0; void *oldtdreadptr = 0; if (verbose) { fprintf(stderr, "Scanning the Kickstart ROM...\n"); } for (i = 8; i < 512; i += 2) { static const u_int8_t header[] = "\xff\xff\xff\xff\x0d\x0a\x0a" "AMIGA ROM Operating System"; u_int8_t * const p = (u_int8_t *) rom + i; if (!memcmp(p, header, sizeof(header) - 1)) { newtdread = p; if (verbose > 1) { fprintf(stderr, "0x%06x: Patch position\n", (u_int8_t *) newtdread - (u_int8_t *) rom + ROMSTART); } break; } } for (i = 8; i < ROMSIZE - 40; i += 2) { u_int8_t * const p = (u_int8_t *) rom + i; u_int32_t p1, p2; p1 = rl(p); p2 = rl(p + 8); if (romptr(p1) && romptr(p2) && rl(p + 4) == p1 && rl(p + 12) == p2 && romptr(rl(p + 16)) && romptr(rl(p + 20)) && rl(p + 24) == p1 && rl(p + 28) == p1 && rl(p + 32) == p1) { const u_int8_t *func = (u_int8_t *) rom + p2 - ROMSTART; if (rl(func) == 0x48E70038 && rl(func + 4) == 0x26690018) { oldtdreadptr = p + 8; if (verbose > 1) { fprintf(stderr, "0x%06x: trackdisk.device CMD_READ function ptr\n", (u_int8_t *) oldtdreadptr - (u_int8_t *) rom + ROMSTART); } break; } } } if (newtdread && oldtdreadptr) { int fd; u_int32_t oldtdread = rl(oldtdreadptr); const u_int32_t offset = (u_int32_t) ((u_int8_t *) newtdread - (u_int8_t *) rom); if (verbose) { fprintf(stderr, "Patching the Kickstart ROM...\n"); } wl(oldtdreadptr, ROMSTART + offset); memcpy(newtdread, patch, sizeof(patch)); wl((u_int8_t *) newtdread + 4, oldtdread); if (verbose) { fprintf(stderr, "Recalculating the Kickstart ROM checksum...\n"); } { u_int8_t *sumptr = (u_int8_t *) rom + ROMSIZE + ROMSUMOFFSET; wl(sumptr, 0); wl(sumptr, wrapsum(rom, ROMSIZE)); if (verbose > 1) { fprintf(stderr, " New ROM checksum 0x%08x\n", rl(sumptr)); } } if (verbose) { fprintf(stderr, "Writing output %s to \"%s\"...\n", kickromonly ? "Kickstart ROM" : "ADF", argv[optind + 1]); } fd = open(argv[optind + 1], O_WRONLY | O_CREAT | O_TRUNC); if (fd != -1) { u_int8_t blk[1024]; if (!kickromonly) { if (bootable) { memcpy(blk, bootblock, bootblocksize); memset(blk + bootblocksize, 0, 512 - bootblocksize); /* Copy beginning of the ROM for checksumming */ memcpy(blk + 512, rom, 512); wl(blk + 4, 0); wl(blk + 4, wrapsum(blk, sizeof(blk))); if (verbose > 1) { fprintf(stderr, " New bootblock checksum 0x%08x\n", rl(blk + 4)); } } else { blk[0] = 'K'; blk[1] = 'I'; blk[2] = 'C'; blk[3] = 'K'; memset(blk + 4, 0, 512 - 4); } if (write(fd, blk, 512) != 512) { perror("write"); goto out; } } if (write(fd, rom, ROMSIZE) != ROMSIZE) { perror("write"); goto out; } if (!kickromonly) { struct datestamp t; /* write zero-blocks between ksrom and rootblock */ memset(blk, 0, 512); i = 11 * 80 - (1 + ROMSIZE / 512); do { if (write(fd, blk, 512) != 512) { perror("write"); goto out; } } while (--i); /* generate rootblock (880) */ mkdatestamp(&t, time(NULL)); memset(blk, 0, 512); wl(blk + 0, 2); /* type = rootblock */ wl(blk + 12, 512 / 4 - 56); /* hashtable size */ wl(blk + 312, 0xffffffff); /* bitmap valid */ wl(blk + 316, 881); /* bitmap sector */ wl(blk + 420, t.days); /* last rootblock alteration timestamp */ wl(blk + 424, t.mins); wl(blk + 428, t.ticks); blk[432] = strlen(volname); memcpy(blk + 433, volname, blk[432]); wl(blk + 472, t.days); /* last disk alteration timestamp */ wl(blk + 476, t.mins); wl(blk + 480, t.ticks); wl(blk + 484, t.days); /* filesystem creation timestamp */ wl(blk + 488, t.mins); wl(blk + 492, t.ticks); wl(blk + 508, 1); /* subtype */ wl(blk + 20, blksum(blk, 512)); if (write(fd, blk, 512) != 512) { perror("write"); goto out; } /* generate bitmap block (881) */ memset(blk, 0, 512); /* add all free blocks */ for (i = 1 + ROMSIZE / 512; i < 880; i++) { freeblk(blk + 4, i); } for (i = 882; i < 880 * 2; i++) { freeblk(blk + 4, i); } wl(blk, blksum(blk, 512)); if (write(fd, blk, 512) != 512) { perror("write"); goto out; } /* write zero-blocks after bitmap block till disk end */ memset(blk, 0, 512); i = 880 * 2 - 882; do { if (write(fd, blk, 512) != 512) { perror("write"); goto out; } } while (--i); } if (verbose) { fprintf(stderr, "All ok\n"); } rc = EXIT_SUCCESS; out: close(fd); } else perror("open for writing"); } else fprintf(stderr, "unable to patch the KS ROM\n"); } if (rom) { free(rom); } return rc; }