--- trunk/src/cpus/cpu_transputer_instr.c 2007/10/08 16:20:26 28 +++ trunk/src/cpus/cpu_transputer_instr.c 2007/10/08 16:20:40 30 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_transputer_instr.c,v 1.2 2006/07/21 05:46:45 debug Exp $ + * $Id: cpu_transputer_instr.c,v 1.6 2006/07/23 19:36:14 debug Exp $ * * INMOS transputer instructions. * @@ -34,24 +34,18 @@ * call. If no instruction was executed, then it should be decreased. If, say, * 4 instructions were combined into one function and executed, then it should * be increased by 3.) + * + * NOTE: The PC does not need to be synched before e.g. memory_rw(), because + * there is no MMU in the Transputer, and hence there can not be any + * exceptions on memory accesses. */ -/* - * nop: Do nothing. - */ -X(nop) -{ -} - - /*****************************************************************************/ /* * j: Relative jump - * - * arg[0] = relative distance from the _current_ instruction. */ X(j) { @@ -59,23 +53,320 @@ int low_pc = ((size_t)ic - (size_t)cpu->cd.transputer.cur_ic_page) / sizeof(struct transputer_instr_call); cpu->pc &= ~(TRANSPUTER_IC_ENTRIES_PER_PAGE-1); - cpu->pc += low_pc + ic->arg[0]; + cpu->pc += low_pc + ic->arg[0] + 1 + cpu->cd.transputer.oreg; + cpu->cd.transputer.oreg = 0; quick_pc_to_pointers(cpu); } /* - * ldc: Load constant + * ldlp: Load local pointer + */ +X(ldlp) +{ + cpu->cd.transputer.c = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.a; + cpu->cd.transputer.oreg |= ic->arg[0]; + cpu->cd.transputer.a = cpu->cd.transputer.oreg * sizeof(uint32_t) + + cpu->cd.transputer.wptr; + cpu->cd.transputer.oreg = 0; +} + + +/* + * pfix: Prefix * - * arg[0] = constant + * arg[0] = nibble + */ +X(pfix) +{ + cpu->cd.transputer.oreg |= ic->arg[0]; + cpu->cd.transputer.oreg <<= 4; +} + + +/* + * ldnl: Load non-local + */ +X(ldnl) +{ + uint32_t addr, w32; + uint8_t word[sizeof(uint32_t)]; + unsigned char *page; + + cpu->cd.transputer.oreg |= ic->arg[0]; + addr = cpu->cd.transputer.oreg * sizeof(uint32_t) + + cpu->cd.transputer.a; + + page = cpu->cd.transputer.host_load[TRANSPUTER_ADDR_TO_PAGENR(addr)]; + if (page == NULL) { + cpu->memory_rw(cpu, cpu->mem, addr, word, sizeof(word), + MEM_READ, CACHE_DATA); + w32 = *(uint32_t *) &word[0]; + } else { + w32 = *(uint32_t *) &page[TRANSPUTER_PC_TO_IC_ENTRY(addr)]; + } + + cpu->cd.transputer.a = LE32_TO_HOST(w32); + cpu->cd.transputer.oreg = 0; +} + + +/* + * ldc: Load constant */ X(ldc) { - /* TODO: Is oreg really cleared like this? */ - cpu->cd.transputer.oreg = ic->arg[0]; cpu->cd.transputer.c = cpu->cd.transputer.b; cpu->cd.transputer.b = cpu->cd.transputer.a; - cpu->cd.transputer.a = ic->arg[0]; + cpu->cd.transputer.a = cpu->cd.transputer.oreg | ic->arg[0]; + cpu->cd.transputer.oreg = 0; +} + + +/* + * ldnlp: Load non-local pointer + */ +X(ldnlp) +{ + cpu->cd.transputer.a += (cpu->cd.transputer.oreg | ic->arg[0]) + * sizeof(uint32_t); + cpu->cd.transputer.oreg = 0; +} + + +/* + * nfix: Negative prefix + */ +X(nfix) +{ + cpu->cd.transputer.oreg |= ic->arg[0]; + cpu->cd.transputer.oreg = (~cpu->cd.transputer.oreg) << 4; +} + + +/* + * ldl: Load local + */ +X(ldl) +{ + uint32_t addr; + uint8_t word[sizeof(uint32_t)]; + uint32_t w32; + unsigned char *page; + + cpu->cd.transputer.c = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.a; + cpu->cd.transputer.oreg |= ic->arg[0]; + addr = cpu->cd.transputer.oreg * sizeof(uint32_t) + + cpu->cd.transputer.wptr; + + page = cpu->cd.transputer.host_load[TRANSPUTER_ADDR_TO_PAGENR(addr)]; + if (page == NULL) { + cpu->memory_rw(cpu, cpu->mem, addr, word, sizeof(word), + MEM_READ, CACHE_DATA); + w32 = *(uint32_t *) &word[0]; +printf("A w32 = %08"PRIx32"\n", w32); + } else { + w32 = *(uint32_t *) &page[TRANSPUTER_PC_TO_IC_ENTRY(addr)]; +printf("B w32 = %08"PRIx32"\n", w32); + } + + cpu->cd.transputer.a = LE32_TO_HOST(w32); + cpu->cd.transputer.oreg = 0; +} + + +/* + * ajw: Adjust workspace + */ +X(ajw) +{ + cpu->cd.transputer.oreg |= ic->arg[0]; + cpu->cd.transputer.wptr += (cpu->cd.transputer.oreg * sizeof(uint32_t)); + cpu->cd.transputer.oreg = 0; +} + + +/* + * eqc: Equal to constant + */ +X(eqc) +{ + cpu->cd.transputer.a = (cpu->cd.transputer.a == + (cpu->cd.transputer.oreg | ic->arg[0])); + cpu->cd.transputer.oreg = 0; +} + + +/* + * stl: Store local + */ +X(stl) +{ + uint32_t addr, w32; + unsigned char *page; + + w32 = LE32_TO_HOST(cpu->cd.transputer.a); + cpu->cd.transputer.oreg |= ic->arg[0]; + addr = cpu->cd.transputer.oreg * sizeof(uint32_t) + + cpu->cd.transputer.wptr; + + page = cpu->cd.transputer.host_store[TRANSPUTER_ADDR_TO_PAGENR(addr)]; + if (page == NULL) { + cpu->memory_rw(cpu, cpu->mem, addr, (void *)&w32, sizeof(w32), + MEM_WRITE, CACHE_DATA); + } else { + *((uint32_t *) &page[TRANSPUTER_PC_TO_IC_ENTRY(addr)]) = w32; + } + + cpu->cd.transputer.a = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.c; + cpu->cd.transputer.oreg = 0; +} + + +/* + * stnl: Store non-local + */ +X(stnl) +{ + uint32_t addr, w32; + unsigned char *page; + + w32 = LE32_TO_HOST(cpu->cd.transputer.b); + cpu->cd.transputer.oreg |= ic->arg[0]; + addr = cpu->cd.transputer.oreg * sizeof(uint32_t) + + cpu->cd.transputer.a; + + page = cpu->cd.transputer.host_store[TRANSPUTER_ADDR_TO_PAGENR(addr)]; + if (page == NULL) { + cpu->memory_rw(cpu, cpu->mem, addr, (void *)&w32, sizeof(w32), + MEM_WRITE, CACHE_DATA); + } else { + *((uint32_t *) &page[TRANSPUTER_PC_TO_IC_ENTRY(addr)]) = w32; + } + + cpu->cd.transputer.a = cpu->cd.transputer.c; + cpu->cd.transputer.oreg = 0; +} + + +/* + * opr: Operate + * + * TODO/NOTE: This doesn't work too well with the way dyntrans is designed + * to work. Maybe it should be rewritten some day. But how? + * Right now it works almost 100% like an old-style interpretation + * function. + */ +X(opr) +{ + cpu->cd.transputer.oreg |= ic->arg[0]; + + switch (cpu->cd.transputer.oreg) { + + case T_OPC_F_REV: + { + uint32_t tmp = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.a; + cpu->cd.transputer.a = tmp; + } + break; + + case T_OPC_F_ADD: + { + uint32_t old_a = cpu->cd.transputer.a; + cpu->cd.transputer.a = cpu->cd.transputer.b + old_a; + if ((cpu->cd.transputer.a & 0x80000000) != + (cpu->cd.transputer.b & old_a & 0x80000000)) + cpu->cd.transputer.error = 1; + cpu->cd.transputer.b = cpu->cd.transputer.c; + } + break; + + case T_OPC_F_SUB: + { + uint32_t old_a = cpu->cd.transputer.a; + cpu->cd.transputer.a = cpu->cd.transputer.b - old_a; + if ((cpu->cd.transputer.a & 0x80000000) != + (cpu->cd.transputer.b & old_a & 0x80000000)) + cpu->cd.transputer.error = 1; + cpu->cd.transputer.b = cpu->cd.transputer.c; + } + break; + + case T_OPC_F_LDPI: + /* Load pointer to (next) instruction */ + { + int low_pc = ((size_t)ic - + (size_t)cpu->cd.transputer.cur_ic_page) + / sizeof(struct transputer_instr_call); + cpu->pc &= ~(TRANSPUTER_IC_ENTRIES_PER_PAGE-1); + cpu->pc += low_pc; + cpu->cd.transputer.a += cpu->pc + 1; + } + break; + + case T_OPC_F_STHF: + cpu->cd.transputer.fptrreg0 = cpu->cd.transputer.a; + cpu->cd.transputer.a = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.c; + break; + + case T_OPC_F_STLF: + cpu->cd.transputer.fptrreg1 = cpu->cd.transputer.a; + cpu->cd.transputer.a = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.c; + break; + + case T_OPC_F_BCNT: + cpu->cd.transputer.a <<= 2; + break; + + case T_OPC_F_GAJW: + { + uint32_t old_wptr = cpu->cd.transputer.wptr; + cpu->cd.transputer.wptr = cpu->cd.transputer.a & ~3; + cpu->cd.transputer.a = old_wptr; + } + break; + + case T_OPC_F_WCNT: + cpu->cd.transputer.c = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.a & 3; + cpu->cd.transputer.a >>= 2; + break; + + case T_OPC_F_MINT: + cpu->cd.transputer.c = cpu->cd.transputer.b; + cpu->cd.transputer.b = cpu->cd.transputer.a; + cpu->cd.transputer.a = 0x80000000; + break; + + case T_OPC_F_MOVE: + /* TODO: This can be optimized a lot by using the host's + memmove(). The only important thing to consider is + if src or dst crosses a host memblock boundary. */ + { + uint32_t i, src = cpu->cd.transputer.c, + dst = cpu->cd.transputer.b; + uint8_t byte; + for (i=1; i<=cpu->cd.transputer.a; i++) { + cpu->memory_rw(cpu, cpu->mem, src ++, &byte, + 1, MEM_READ, CACHE_DATA); + cpu->memory_rw(cpu, cpu->mem, dst ++, &byte, + 1, MEM_WRITE, CACHE_DATA); + } + } + break; + + default:fatal("UNIMPLEMENTED opr oreg 0x%"PRIx32"\n", + cpu->cd.transputer.oreg); + exit(1); + } + + cpu->cd.transputer.oreg = 0; } @@ -157,9 +448,9 @@ switch (ib[0] >> 4) { - case 0: /* j, relative jump */ + case T_OPC_J: + /* relative jump */ ic->f = instr(j); - ic->arg[0] = (ib[0] & 0xf) + 1; /* TODO: Samepage jump! */ if (cpu->cd.transputer.cpu_type.features & T_DEBUG @@ -174,10 +465,66 @@ } break; - case 4: /* ldc, load constant */ + case T_OPC_LDLP: + /* load local pointer */ + ic->f = instr(ldlp); + break; + + case T_OPC_PFIX: + /* prefix */ + ic->f = instr(pfix); + break; + + case T_OPC_LDNL: + /* load non-local */ + ic->f = instr(ldnl); + break; + + case T_OPC_LDC: + /* load constant */ ic->f = instr(ldc); break; + case T_OPC_LDNLP: + /* load non-local pointer */ + ic->f = instr(ldnlp); + break; + + case T_OPC_NFIX: + /* negative prefix */ + ic->f = instr(nfix); + break; + + case T_OPC_LDL: + /* load local */ + ic->f = instr(ldl); + break; + + case T_OPC_AJW: + /* adjust workspace */ + ic->f = instr(ajw); + break; + + case T_OPC_EQC: + /* equal to constant */ + ic->f = instr(eqc); + break; + + case T_OPC_STL: + /* store local */ + ic->f = instr(stl); + break; + + case T_OPC_STNL: + /* store non-local */ + ic->f = instr(stnl); + break; + + case T_OPC_OPR: + /* operate */ + ic->f = instr(opr); + break; + default:fatal("UNIMPLEMENTED opcode 0x%02x\n", ib[0]); goto bad; }