Z80: update the banking logic based on the SDCC changes made
authorAlan Cox <alan@linux.intel.com>
Sun, 8 Feb 2015 15:21:25 +0000 (15:21 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 8 Feb 2015 15:21:25 +0000 (15:21 +0000)
Kernel/doc/SDCCBanking
Kernel/tools/binmunge.c

index 44d2d0e..0e83f9a 100644 (file)
@@ -6,6 +6,14 @@ code. This avoids the need to specify far functions and banks inline in the
 code itself an allows us to fix up the banking semi-automatically. Currently
 it's not smart enough to lay out the banks itself.
 
+The compiler is also modified to add an option --external-banker which
+causes it to do two things
+
+1.     Calls to functions are generated with push af/pop af either side
+       of the invocation (except calls to literals)
+
+2.     Functions assume there are four bytes of pushed address
+
 
 How it works
 
@@ -45,132 +53,56 @@ images.
 binmunge reads the relocs and banks and converts as follows
 
 Code
-       0xCD (CALL) in the byte before a relocation is patched to an RST
-       RST8 - bank 1
-       RST10 - bank 2
-       RST18 - bank 3
+       PUSH AF
+       CALL xx
+       POP AF
+
+       is turned into
+
+       CALL __bank_n_from_m    ; for inter-bank calls
+       DEFW xx
 
        0xC3 (JUMP) is turned into a jump into a stub
 
        Anything else is considered an error
 
 Data
-       Each entry is turned into a stub. Identical entries are turned into
-       the same stub call
+       Each code entry is turned into a stub. Identical entries are turned
+       into the same stub call from the same bank (They are assumed to be
+       function pointers)
 
 Only 16bit relocations are processed. Weird tricks like &function >> 8 may
 break. Hopefully SDCC never decides to create one behind our back.
 
+Stubs:
 
-Stubs
-
-The scripts scan for a _STUBS section. This is overwritten by the linker
-with 4 bytes per stub needed. Each stub takes the form
-
-       RSTxx
-       .dw address             ; real target address
+       CALL __bank_n_from_m
+       defw xx
        RET
 
-and the address in the data is replaced with the address of the stub.
-
+The stubs may live in the bank of the invoking function, while __bank_x is
+common
 
 This is done to process tables. It means that function tables like device or
 syscall tables correctly generate inter bank calls.
 
-
-
-RST Handlers
-
-SDCC is completely unaware of the banking. While this makes near calls
-faster and avoids all the far stack crap, it means that some stack games are
-needed in the stubs in order to restore the previous bank on return. In
-particular because SDCC always uses stack based argument passing the stack
-needs shuffling.
-
-We have two options, one is to shuffle the stack around (and hide the extra
-info in the stack), the other is to keep a separate bank stack, remembering
-however that we regularly use other stacks and reload sp -> not fun.
-
-
-So we end up needing something like
-
-;
-;      On entry A is the target bank number, all other registers are
-;      cannon fodder except for IX (although we can trash IX providing
-;      we restore it on retv)
-;
-rstbank:
-       pop hl          ; return address (pointer to real call vector)
-       pop bc          ; first argument
-       exx
-       pop hl          ; arguments 2-4 (potentially)
-       pop de
-       pop bc
-       exx
-       ld e, (hl)      ; get the call target
-       inc hl
-       ld d, (hl)
-       inc hl
-       push hl         ; save the true return address 
-;
-;      We could take an IRQ after loading a with rombank but the IRQ
-;      return path will always put us back in the same bank, so the
-;      answer will still be correct at the end of the interrupt handler
-;
-       ld hl, rombank  ; save the banking setting
-       ld h, (hl)      ; doing it this way around we can
-       push hl         ; preserve A 
-       exx             ; push up to 4 arguments back
-       push bc
-       push de
-       push hl
-       exx
-       push bc
-       ld hl, #retv    ; push our return address
-       push hl
-       ex de, hl
-;
-;      The ordering here is critical. If we take an IRQ as we bank the
-;      irq restore may see the rombank write before we do the out. This way
-;      around it will restore the new bank, and our out will be a no-op
-;
-       ld (rombank), a
-       out (bank), a
-       jp (hl)         ; invoke the banked C method
-
-retv:  pop de          ; pull off 4 arguments in DE and HLDEBC'
-       exx
-       pop hl  
-       pop de
-       pop bc
-       exx
-       pop af          ; pull off the banking info
-       ; Ordering here is also vital for IRQ
-       ld bc, #0x7ffd  
-       ld (rombank), a ; unbank
-       out (c), a
-       pop bc          ; recover real return address in BC
-       push de         ; now push the 4 potential arguments back
-       exx
-       push bc
-       push de
-       push hl
-       exx
-       push bc         ; hl must be preserved so return via push bc/ret
-       ret
-
-       .org 8
-rst8:  ld a, #0x18     ; Screen in bank 7, 48K rom present, bank 0
-       jp rstbank
-
-       .org 0x10
-rst10: ld a, #0x19     ; Screen in bank 7, 48K rom present, bank 1
-       jp rstbank
-
-       .org 0x18
-rst18: ld a, #23       ; Screen in bank 7, 48K rom present, bank 7
-       jp rstbank
-
-
-
-
+Banking Handlers
+
+
+__bank_0_from_1:
+       POP HL                  ; return
+       LD DE, (HL)
+       INC HL
+       LD D, (HL)
+       INC HL
+       PUSH HL                 ; true return
+       LD A, #bank0op
+       OUT (port), A
+       EX DE, HL
+       CALL __callhl
+__bank1_ret:
+       LD A, #1
+       OUT (port), A
+       RET
+__callhl:
+       JP (HL)
\ No newline at end of file
index 67bca37..028356c 100644 (file)
@@ -1,7 +1,5 @@
 /*
  *     Fix up the given binary block with the reloc data
- *
- *     Patch RST lines into the relevant places, generate stub info
  */
 
 #include <stdio.h>
@@ -23,9 +21,18 @@ static int nextfix;
 static int lastfix;
 static int stub_all;
 
+struct symbol {
+  struct symbol *next;
+  char *name;
+  unsigned int val;
+};
+
+struct symbol *symbols;
+
 struct stubmap {
   struct stubmap *next;
-  int bank;
+  int sbank;
+  int dbank;
   uint16_t addr;
   uint16_t target;
 };
@@ -45,14 +52,43 @@ unsigned int resize(int b)
   return size[b];
 }
 
+void add_symbol(char *name, unsigned int val)
+{
+  struct symbol *s = malloc(sizeof(struct symbol) + strlen(name) + 1);
+  if (s == NULL) {
+    fprintf(stderr, "Out of memory.\n");
+    exit(1);
+  }
+  s->name = (char *)(s + 1);
+  s->val = val;
+  strcpy(s->name, name);
+  s->next = symbols;
+  symbols = s;
+}
+  
+unsigned int get_bank_function(int sbank, int dbank)
+{
+   struct symbol *s = symbols; 
+   char name[32];
+   sprintf(name, "__bank_%d_%d", sbank, dbank);
+   while (s) {
+     if (strcmp(s->name, name) == 0)
+       return s->val;
+     s = s->next;
+  }
+  fprintf(stderr, "Symbol '%s' is missing for banked use.\n", name);
+  exit(1);
+}
+
 /* FIXME: */
-int stubmap(uint16_t v, int bank)
+int stubmap(uint16_t v, int sbank, int dbank)
 {
   struct stubmap *s = stubs;
   struct stubmap **p = &stubs;
+  uint16_t da;
 
   while(s) {
-    if (s->bank == bank && s->addr == v) {
+    if (s->sbank == sbank && s->addr == v && s->dbank == dbank) {
       stubdup++;
       return s->target;
     }
@@ -66,17 +102,27 @@ int stubmap(uint16_t v, int bank)
   }
   *p = s;
   s->next = NULL;
-  s->bank = bank;
+  s->sbank = sbank;
+  s->dbank = dbank;
   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 */
+  /* FIXME: we could have per bank stubs in ROM and not waste precious
+     common memory here */
+  da = get_bank_function(sbank, dbank);
+  /* Call */
+  buf[0][nextfix++] = 0xCD;
+  /* Bank function */
+  buf[0][nextfix++] = da & 0xFF;
+  buf[0][nextfix++] = da >> 8;
+  /* Defw address */
   buf[0][nextfix++] = v & 0xFF;        /* Target */
   buf[0][nextfix++] = v >> 8;
-  buf[0][nextfix++] = 0xC9;    /* RET */
+  /* Ret */
+  buf[0][nextfix++] = 0xC9;
   stubct++;
   return s->target;
 }
@@ -95,21 +141,31 @@ void code_reloc(uint8_t sbank, uint16_t ptr, uint8_t dbank)
   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);
+        printf("Converting JP at %04x to stub\n", ptr);
+      da = stubmap(buf[sbank][ptr] + (buf[sbank][ptr+1] << 8), sbank, dbank);
       buf[sbank][ptr] = da & 0xFF;
       buf[sbank][ptr+1] = da >> 8;
       break;
-    case 0xCD: /* CALL */
+    case 0xF5: /* PUSH AF CALL POP AF */
+      if (buf[sbank][ptr] != 0xCD) {
+        fprintf(stderr, "Bad format for relocated long call at%04x\n", ptr);
+        exit(1);
+      }
       if (v)
-        printf("Converting CALL at %04x to RST\n", ptr);
-      buf[sbank][ptr-1] = 0xC7 + 8 * dbank;    /* RST 8/16/24 */
+        printf("Converting CALL at %04x\n", ptr);
+      /* Turn the push af into a call */
+      buf[sbank][ptr-1] = 0xCD;
+      /* Move the address along */
+      buf[sbank][ptr+3] = buf[sbank][ptr+2];
+      buf[sbank][ptr+2] = buf[sbank][ptr+1];
+      /* Fit in the actual call target */
+      da = get_bank_function(sbank, dbank);
+      /* Sequence is now CALL __bank_sbank_dbank DW target */
+      buf[sbank][ptr] = da & 0xFF;
+      buf[sbank][ptr+1] = da >> 8;
       break;
-    case 0xC7: /* RST */
-    case 0xCF:
-    case 0xD7:
-    case 0xDF:
-      fprintf(stderr, "File already processed!\n");
+    case 0xCD:
+      fprintf(stderr, "File already processed or short call at %04x!\n", ptr);
       exit(1);
     default:
       fprintf(stderr, "Bad relocation in code %04X: %02X\n",
@@ -128,7 +184,7 @@ void data_reloc(uint8_t sbank, uint16_t ptr, uint8_t dbank)
   if (v)
     printf("Stubhooking %04x for data reference.\n", ptr);  
   /* Find the stub for it */
-  na = stubmap(n, dbank);
+  na = stubmap(n, sbank, dbank);
   if (na == -1) {
     fprintf(stderr, "No stub match: stubs stale\n");
     exit(1);
@@ -178,6 +234,26 @@ static void process_stub(char *p)
     data_reloc(b1, addr, b2);
 }
 
+static void scan_symbols(FILE *f)
+{
+  char buf[256];
+  char *sym;
+  unsigned int addr;
+
+  while(fgets(buf, 255, stdin)) {
+    if (memcmp(buf, "      0000", 10))
+      continue;
+    /* Looks like a symbol */
+    if (sscanf(buf+6, "%x", &addr) != 1)
+      continue;
+    /* Smells like a symbol */
+    sym = strtok(buf+16, " \n\t");
+    if (!sym)
+      continue;
+    /* Guess it's a symbol then */
+    add_symbol(sym, addr);
+  }
+}    
 
 int main(int argc, char *argv[])
 {
@@ -188,7 +264,7 @@ int main(int argc, char *argv[])
   int sb, ss;
   int opt;
 
-  while ((opt = getopt(argc, "av")) != -1) {
+  while ((opt = getopt(argc, argv, "av")) != -1) {
     switch (opt) {
       case 'a':
         stub_all = 1;
@@ -210,6 +286,15 @@ int main(int argc, char *argv[])
   nextfix = sb;
   lastfix = sb + (ss & ~3);
   
+  r = fopen("fuzix.map", "r");
+  if (r == NULL) {
+    perror("fuzix.map");
+    exit(1);
+  }
+  scan_symbols(r);
+  fclose(r);
+  
   r = fopen("relocs.dat", "r");
   if (r == NULL) {
     perror("relocs.dat");