From: Alan Cox Date: Sun, 8 Feb 2015 15:21:25 +0000 (+0000) Subject: Z80: update the banking logic based on the SDCC changes made X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=cf0c0d1da554ae785200da95187a2e028a36c856;p=FUZIX.git Z80: update the banking logic based on the SDCC changes made --- diff --git a/Kernel/doc/SDCCBanking b/Kernel/doc/SDCCBanking index 44d2d0ee..0e83f9a0 100644 --- a/Kernel/doc/SDCCBanking +++ b/Kernel/doc/SDCCBanking @@ -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 diff --git a/Kernel/tools/binmunge.c b/Kernel/tools/binmunge.c index 67bca37c..028356c6 100644 --- a/Kernel/tools/binmunge.c +++ b/Kernel/tools/binmunge.c @@ -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 @@ -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");