binmunge, bihx: SDCC banking hacks
authorAlan Cox <alan@linux.intel.com>
Wed, 31 Dec 2014 22:01:43 +0000 (22:01 +0000)
committerAlan Cox <alan@linux.intel.com>
Wed, 31 Dec 2014 22:01:43 +0000 (22:01 +0000)
These two tools are the first parts of the hacks to make SDCC generate banked
binaries. It's not a full automatic banker but it's a first step.

sdldz80 is modified to spit out a bihx format, which is a file that contains
lines of the format

XX:Intel IHX - Intel ihex from the : for bank XX
:Intel IHX  - Intel ihex for all the banks used
Bxxxx - Inter bank relocation record

Currently it is hard coded to understand the following segments

CODE "bank 1" - banked code
CODE2 "bank 2" - banked code
CODE3 "bank 3" - banked code
VIDEO "bank 3" - banked code
FONT "bank 3" - data
INITIALIZER common  - data
DATA common  - data
CONST common  - data
COMMONMEM common  - code
DISCARD "bank 3" - banked (discard) code

calls are patched from CD xx xx to use RST8/16/24 xx xx according to the
bank in question. References to banked objects that are made from data
areas or are JP rather than call instructions are fixed up by changing the
vector to point to a 4 byte stub for each problem case that is of the form
RST xx xx RET.

This generates more stubs than we really need right now. Most of them will go
away if the system call table is moved into its own file and we add some
kind of 'DATA2' segment.

Kernel/tools/bihx.c [new file with mode: 0644]
Kernel/tools/binmunge.c [new file with mode: 0644]

diff --git a/Kernel/tools/bihx.c b/Kernel/tools/bihx.c
new file mode 100644 (file)
index 0000000..b94b18a
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *     Banked ihx processor
+ *
+ *     Split the ihx file into base and bank blocks
+ *     Perform the patch ups on the file
+ *     Write the stubs
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+FILE *fseg[10];
+static char fbuf[64];
+
+void open_fseg(int n)
+{
+  if (n == 0)
+    sprintf(fbuf, "common.ihx");
+  else
+    sprintf(fbuf, "bank%d.ihx", n);
+  fseg[n] = fopen(fbuf, "w");
+  if (fseg[n] == NULL) {
+    perror(fbuf);
+    exit(1);
+  }
+}
+
+void put_seg(int n, char *bp)
+{
+  if (fseg[n] == NULL)
+    open_fseg(n);
+  fputs(bp, fseg[n]);
+}
+
+void put_all_segs(char *bp)
+{
+  int i;
+  for (i = 0; i < 10; i++) {
+    if (fseg[i])
+      fputs(bp, fseg[i]);
+  }
+}
+
+void close_segs(void)
+{
+  int i;
+  for (i = 0; i < 10; i++) {
+    if (fseg[i])
+      if(fclose(fseg[i]))
+        perror("fclose");
+  }
+}
+
+void bin_segs(void)
+{
+  int i;
+  system("makebin -s 65536 -p common.ihx >common.bin\n");
+  for (i = 1; i < 10; i++) {
+    if (fseg[i]) {
+      snprintf(fbuf, 64, "makebin -s 65536 -p bank%d.ihx >bank%d.bin\n",
+        i, i);
+      system(fbuf);
+    }
+  }
+}
+
+void save_patch_rule(FILE *fr, char *buf)
+{
+  fputs(buf, fr);
+}
+
+void split_bihx(char *name)
+{
+  int n;
+  char buf[256];
+  FILE *fp = fopen(name, "r");
+  FILE *fr;
+
+  if (fp == NULL) {
+    perror(name);
+    exit(1);
+  }
+  
+  fr = fopen("relocs.dat", "w");
+  if (fr == NULL) {
+    perror("relocs.dat");
+    exit(1);
+  }
+
+  while(fgets(buf, 256, fp)) {
+    /* Leading hex means ihx data for this target */
+    if (isdigit(*buf)) {
+      sscanf(buf, "%x", &n);
+      put_seg(n, buf + 2);
+    } else if (*buf == ':')    /* ihx for all (end marker) */
+      put_all_segs(buf);       
+    else if (*buf == ';')
+      continue;
+    else if (*buf == 'B')
+      save_patch_rule(fr, buf);
+    else
+      fprintf(stderr, "%s: invalid bihx line.\n", name, buf);
+  }
+  fclose(fp);
+  fclose(fr);
+}
+
+int main(int argc, char *argv[])
+{
+  if (argc != 2) {
+    fprintf(stderr, "%s bihxfile\n", argv[0]);
+    exit(1);
+  }
+  split_bihx(argv[1]);
+  close_segs();
+  bin_segs();
+}
+
+      
+      
\ No newline at end of file
diff --git a/Kernel/tools/binmunge.c b/Kernel/tools/binmunge.c
new file mode 100644 (file)
index 0000000..4a7d4e3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *     Fix up the given binary block with the reloc data
+ *
+ *     Patch RST lines into the relevant places, generate stub info
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define NBANKS 4
+
+unsigned char buf[NBANKS][65536];
+unsigned int size[NBANKS];
+FILE *fptr[NBANKS];
+int v;
+static int nextfix = 0xFE00;   /* FIXME */
+static int lastfix = 0x10000;
+
+struct stubmap {
+  struct stubmap *next;
+  int bank;
+  uint16_t addr;
+  uint16_t target;
+};
+
+struct stubmap *stubs;
+static int stubct = 0;
+static int stubdup = 0;
+
+unsigned int resize(int b)
+{
+  unsigned char *bp = buf[b] + 65535;
+  while(*bp == 0)
+    bp--;
+  bp++;
+  if (bp - buf[b] > size[b])
+    return bp - buf[b];
+  return size[b];
+}
+
+/* FIXME: */
+int stubmap(uint16_t v, int bank)
+{
+  struct stubmap *s = stubs;
+  struct stubmap **p = &stubs;
+
+  while(s) {
+    if (s->bank == bank && s->addr == v) {
+      stubdup++;
+      return s->target;
+    }
+    p = &s->next;
+    s = *p;
+  }
+  s = malloc(sizeof(struct stubmap));
+  if (s == NULL) {
+    fprintf(stderr, "Out of memory.\n");
+    exit(1);
+  }
+  *p = s;
+  s->next = NULL;
+  s->bank = bank;
+  s->addr = v;
+  s->target = nextfix;
+  if (nextfix == lastfix) {
+    fprintf(stderr, "Out of fix space (%d stubs used).\n", stubct);
+    exit(1);
+  }
+  buf[0][nextfix++] = 0xC7 + 8 * bank; /* RST */
+  buf[0][nextfix++] = v & 0xFF;        /* Target */
+  buf[0][nextfix++] = v >> 8;
+  buf[0][nextfix++] = 0xC9;    /* RET */
+  stubct++;
+  return s->target;
+}
+
+void code_reloc(uint8_t sbank, uint16_t ptr, uint8_t dbank)
+{
+  int da;
+  if (ptr == 0) {
+    fprintf(stderr, "Nonsense zero based code relocation.\n");
+    exit(1);
+  }
+  if (dbank == 0 || dbank >= NBANKS) {
+    fprintf(stderr, "Invalid bank %d\n", dbank);
+    exit(1);
+  }
+  switch(buf[sbank][ptr-1]) {
+    case 0xC3: /* JP - needs stub */
+      if (v)
+        printf("Converting JP at %04x to RST/RET stub\n", ptr);
+      da = stubmap(buf[sbank][ptr] + (buf[sbank][ptr+1] << 8), dbank);
+      buf[sbank][ptr] = da & 0xFF;
+      buf[sbank][ptr+1] = da >> 8;
+      break;
+    case 0xCD: /* CALL */
+      if (v)
+        printf("Converting CALL at %04x to RST\n", ptr);
+      buf[sbank][ptr-1] = 0xC7 + 8 * dbank;    /* RST 8/16/24 */
+      break;
+    case 0xC7: /* RST */
+    case 0xCF:
+    case 0xD7:
+    case 0xDF:
+      fprintf(stderr, "File already processed!\n");
+      exit(1);
+    default:
+      fprintf(stderr, "Bad relocation in code %04X: %02X\n",
+        ptr-1, buf[sbank][ptr-1]);
+  }
+}
+
+void data_reloc(uint8_t sbank, uint16_t ptr, uint8_t dbank)
+{
+  int na;
+  uint16_t n;
+  
+  /* Get the target */
+  n = buf[sbank][ptr] + (buf[sbank][ptr+1]<<8);
+
+  if (v)
+    printf("Stubhooking %04x for data reference.\n", ptr);  
+  /* Find the stub for it */
+  na = stubmap(n, dbank);
+  if (na == -1) {
+    fprintf(stderr, "No stub match: stubs stale\n");
+    exit(1);
+  }
+  /* Patch in the revised destination address */
+  buf[sbank][ptr] = na & 255;
+  buf[sbank][ptr+1] = na >> 8;
+}
+
+int stub_code(char *name)
+{
+  if(strncmp(name, "_CODE", 5) == 0)
+    return 1;
+  if(strcmp(name, "_VIDEO") == 0)
+    return 1;
+  if(strcmp(name, "_COMMONMEM") == 0)
+    return 1;
+  if(strcmp(name, "_DISCARD") == 0)
+    return 1;
+  /* Data */
+  if(strcmp(name, "_INITIALIZER") == 0)
+    return 0;
+  if(strcmp(name, "_DATA") == 0)
+    return 0;
+  if(strcmp(name, "_FONT") == 0)
+    return 0;
+  if(strcmp(name , "_CONST") == 0)
+    return 0;
+  fprintf(stderr, "Unknown bank name %s\n", name);
+  exit(1);
+}
+
+static void process_stub(char *p)
+{
+  int b1, b2, addr;
+  char name[65];
+  if (sscanf(p, "%02x %04x %02x %64s", &b1, &addr, &b2, name) != 4) {
+    fprintf(stderr, "Invalid relocation link %s\n", p);
+    exit(1);
+  }
+  if (stub_code(name))
+    code_reloc(b1, addr, b2);
+  else
+    data_reloc(b1, addr, b2);
+}
+
+
+int main(int argc, char *argv[])
+{
+  FILE *r;
+  char in[256];
+  char bin[64];
+  int banks;
+
+  if (argv[1] && strcmp(argv[1], "-v") == 0)
+    v = 1;
+  if (argc != 1 + v) {
+    fprintf(stderr, "%s -v\n", argv[0]);
+    exit(1);
+  }
+  
+  r = fopen("relocs.dat", "r");
+  if (r == NULL) {
+    perror("relocs.dat");
+    exit(1);
+  }
+
+  for (banks = 0; banks < 4; banks ++) {  
+    if (banks == 0)
+    strcpy(bin, "common.bin");
+    else
+      sprintf(bin, "bank%d.bin", banks);
+    fptr[banks] = fopen(bin, "r+");
+    if (fptr[banks] == NULL) {
+      if (errno != ENOENT) {
+        perror(bin);
+        exit(1);
+      }
+    } else {
+      size[banks] = fread(buf[banks], 1, 65536, fptr[banks]);
+      if (size[banks] < 1) {
+        perror(bin);
+        exit(1);
+      }
+      rewind(fptr[banks]);
+    }
+  }
+  
+  while(fgets(in, 256, r)) {
+    if (*in != 'B') {
+      fprintf(stderr, "Bad record: %s", in);
+      exit(1);
+    }
+    if (v)
+      printf("|%s", in);
+    process_stub(in + 1);
+  }
+  fclose(r);
+
+  for (banks = 0; banks < 4; banks++) {
+    if (fptr[banks]) {
+      /* Just conceivably we might reloc a trailing zero byte and need to grow the
+       file */
+      size[banks] = resize(banks);
+      if (fwrite(buf[banks], size[banks], 1, fptr[banks]) != 1) {
+        perror("fwrite");
+        exit(1);
+      }
+      fclose(fptr[banks]);
+    }
+  }
+  printf("%d stub relocations using %d bytes, %d duplicates\n",
+    stubct, stubct * 4, stubdup);
+  exit(0);
+}