| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include "runtime.h" |
| |
| void abort(void); |
| |
| static void |
| fabort(void) |
| { |
| if (1) { |
| printf("Unsupported floating point instruction\n"); |
| abort(); |
| } |
| } |
| |
| static uint32 doabort = 0; |
| static uint32 trace = 0; |
| |
| #define DOUBLE_EXPBIAS 1023 |
| #define SINGLE_EXPBIAS 127 |
| |
| static const int8* opnames[] = { |
| // binary |
| "adf", |
| "muf", |
| "suf", |
| "rsf", |
| "dvf", |
| "rdf", |
| "pow", |
| "rpw", |
| "rmf", |
| "fml", |
| "fdv", |
| "frd", |
| "pol", |
| "UNDEFINED", |
| "UNDEFINED", |
| "UNDEFINED", |
| |
| // unary |
| "mvf", |
| "mnf", |
| "abs", |
| "rnd", |
| "sqt", |
| "log", |
| "lgn", |
| "exp", |
| "sin", |
| "cos", |
| "tan", |
| "asn", |
| "acs", |
| "atn", |
| "urd", |
| "nrm" |
| }; |
| |
| static const int8* fpconst[] = { |
| "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", |
| }; |
| |
| static const uint64 fpdconst[] = { |
| 0x0000000000000000ll, |
| 0x3ff0000000000000ll, |
| 0x4000000000000000ll, |
| 0x4008000000000000ll, |
| 0x4010000000000000ll, |
| 0x4014000000000000ll, |
| 0x3fe0000000000000ll, |
| 0x4024000000000000ll |
| }; |
| |
| static const int8* fpprec[] = { |
| "s", "d", "e", "?" |
| }; |
| |
| static uint32 |
| precision(uint32 i) |
| { |
| switch (i&0x00080080) { |
| case 0: |
| return 0; |
| case 0x80: |
| return 1; |
| default: |
| fabort(); |
| } |
| return 0; |
| } |
| |
| static uint64 |
| frhs(uint32 rhs) |
| { |
| if (rhs & 0x8) { |
| return fpdconst[rhs&0x7]; |
| } else { |
| return m->freg[rhs&0x7]; |
| } |
| } |
| |
| static int32 |
| fexp(uint64 f) |
| { |
| return (int32)((uint32)(f >> 52) & 0x7ff) - DOUBLE_EXPBIAS; |
| } |
| |
| static uint32 |
| fsign(uint64 f) |
| { |
| return (uint32)(f >> 63) & 0x1; |
| } |
| |
| static uint64 |
| fmantissa(uint64 f) |
| { |
| return f &0x000fffffffffffffll; |
| } |
| |
| static void |
| fprint() |
| { |
| uint32 i; |
| for (i = 0; i < 8; i++) { |
| printf("\tf%d:\t%X\n", i, m->freg[i]); |
| } |
| } |
| |
| static uint32 |
| d2s(uint64 d) |
| { |
| return (d>>32 & 0x80000000) | //sign |
| ((uint32)(fexp(d) + SINGLE_EXPBIAS) & 0xff) << 23 | // exponent |
| (d >> 29 & 0x7fffff); // mantissa |
| } |
| |
| static uint64 |
| s2d(uint32 s) |
| { |
| return (uint64)(s & 0x80000000) << 63 | // sign |
| (uint64)((s >> 23 &0xff) + (DOUBLE_EXPBIAS - SINGLE_EXPBIAS)) << 52 | // exponent |
| (uint64)(s & 0x7fffff) << 29; // mantissa |
| } |
| |
| // cdp, data processing instructions |
| static void |
| dataprocess(uint32* pc) |
| { |
| uint32 i, opcode, unary, dest, lhs, rhs, prec; |
| uint32 high; |
| uint64 fraw0, fraw1, exp, sign; |
| uint64 fd, f0, f1; |
| |
| i = *pc; |
| |
| // data processing |
| opcode = i>>20 & 15; |
| unary = i>>15 & 1; |
| |
| dest = i>>12 & 7; |
| lhs = i>>16 & 7; |
| rhs = i & 15; |
| |
| prec = precision(i); |
| if (unary) { |
| switch (opcode) { |
| case 0: // mvf |
| m->freg[dest] = frhs(rhs); |
| goto ret; |
| default: |
| goto undef; |
| } |
| } else { |
| switch (opcode) { |
| case 1: // muf |
| fraw0 = m->freg[lhs]; |
| fraw1 = frhs(rhs); |
| f0 = fraw0>>21 & 0xffffffff | 0x80000000; |
| f1 = fraw1>>21 & 0xffffffff | 0x80000000; |
| fd = f0*f1; |
| high = fd >> 63; |
| if (high) |
| fd = fd >> 11 & 0x000fffffffffffffll; |
| else |
| fd = fd >> 10 & 0x000fffffffffffffll; |
| exp = (uint64)(fexp(fraw0) + fexp(fraw1) + !!high + DOUBLE_EXPBIAS) & 0x7ff; |
| sign = fraw0 >> 63 ^ fraw1 >> 63; |
| fd = sign << 63 | exp <<52 | fd; |
| m->freg[dest] = fd; |
| goto ret; |
| default: |
| goto undef; |
| } |
| } |
| |
| undef: |
| doabort = 1; |
| |
| ret: |
| if (trace || doabort) { |
| printf(" %p %x\t%s%s\tf%d, ", pc, *pc, opnames[opcode | unary<<4], |
| fpprec[prec], dest); |
| if (!unary) |
| printf("f%d, ", lhs); |
| if (rhs & 0x8) |
| printf("#%s\n", fpconst[rhs&0x7]); |
| else |
| printf("f%d\n", rhs&0x7); |
| } |
| if (doabort) |
| fabort(); |
| } |
| |
| #define CPSR 14 |
| #define FLAGS_N (1 << 31) |
| #define FLAGS_Z (1 << 30) |
| #define FLAGS_C (1 << 29) |
| |
| // cmf, compare floating point |
| static void |
| compare(uint32 *pc, uint32 *regs) { |
| uint32 i, flags, lhs, rhs, sign0, sign1; |
| uint32 f0, f1, mant0, mant1; |
| int32 exp0, exp1; |
| |
| i = *pc; |
| flags = 0; |
| lhs = i>>16 & 0x7; |
| rhs = i & 0xf; |
| |
| f0 = m->freg[lhs]; |
| f1 = frhs(rhs); |
| if (f0 == f1) { |
| flags = FLAGS_Z | FLAGS_C; |
| goto ret; |
| } |
| |
| sign0 = fsign(f0); |
| sign1 = fsign(f1); |
| if (sign0 == 1 && sign1 == 0) { |
| flags = FLAGS_N; |
| goto ret; |
| } |
| if (sign0 == 0 && sign1 == 1) { |
| flags = FLAGS_C; |
| goto ret; |
| } |
| |
| if (sign0 == 0) { |
| exp0 = fexp(f0); |
| exp1 = fexp(f1); |
| mant0 = fmantissa(f0); |
| mant1 = fmantissa(f1); |
| } else { |
| exp0 = fexp(f1); |
| exp1 = fexp(f0); |
| mant0 = fmantissa(f1); |
| mant1 = fmantissa(f0); |
| } |
| |
| if (exp0 > exp1) { |
| flags = FLAGS_C; |
| } else if (exp0 < exp1) { |
| flags = FLAGS_N; |
| } else { |
| if (mant0 > mant1) |
| flags = FLAGS_C; |
| else |
| flags = FLAGS_N; |
| } |
| |
| ret: |
| if (trace) { |
| printf(" %p %x\tcmf\tf%d, ", pc, *pc, lhs); |
| if (rhs & 0x8) |
| printf("#%s\n", fpconst[rhs&0x7]); |
| else |
| printf("f%d\n", rhs&0x7); |
| } |
| regs[CPSR] = regs[CPSR] & 0x0fffffff | flags; |
| } |
| |
| // ldf/stf, load/store floating |
| static void |
| loadstore(uint32 *pc, uint32 *regs) |
| { |
| uint32 i, isload, coproc, ud, wb, tlen, p, reg, freg, offset; |
| uint32 addr; |
| |
| i = *pc; |
| coproc = i>>8&0xf; |
| isload = i>>20&1; |
| p = i>>24&1; |
| ud = i>>23&1; |
| tlen = i>>(22 - 1)&1 | i>>15&1; |
| wb = i>>21&1; |
| reg = i>>16 &0xf; |
| freg = i>>12 &0x7; |
| offset = (i&0xff) << 2; |
| |
| if (coproc != 1 || p != 1 || wb != 0 || tlen > 1) |
| goto undef; |
| if (reg > 13) |
| goto undef; |
| |
| if (ud) |
| addr = regs[reg] + offset; |
| else |
| addr = regs[reg] - offset; |
| |
| if (isload) |
| if (tlen) |
| m->freg[freg] = *((uint64*)addr); |
| else |
| m->freg[freg] = s2d(*((uint32*)addr)); |
| else |
| if (tlen) |
| *((uint64*)addr) = m->freg[freg]; |
| else |
| *((uint32*)addr) = d2s(m->freg[freg]); |
| goto ret; |
| |
| undef: |
| doabort = 1; |
| |
| ret: |
| if (trace || doabort) { |
| if (isload) |
| printf(" %p %x\tldf", pc, *pc); |
| else |
| printf(" %p %x\tstf", pc, *pc); |
| printf("%s\t\tf%d, %s%d(r%d)", fpprec[tlen], freg, ud ? "" : "-", offset, reg); |
| printf("\t\t// %p", regs[reg] + (ud ? offset : -offset)); |
| if (coproc != 1 || p != 1 || wb != 0) |
| printf(" coproc: %d pre: %d wb %d", coproc, p, wb); |
| printf("\n"); |
| fprint(); |
| } |
| if (doabort) |
| fabort(); |
| } |
| |
| static void |
| loadconst(uint32 *pc, uint32 *regs) |
| { |
| uint32 offset; |
| uint32 *addr; |
| |
| if (*pc & 0xfffff000 != 0xe59fb838 || |
| *(pc+1) != 0xe08bb00c || |
| *(pc+2) & 0xffff8fff != 0xed9b0100) |
| goto undef; |
| |
| offset = *pc & 0xfff; |
| addr = (uint32*)((uint8*)pc + offset + 8); |
| //printf("DEBUG: addr %p *addr %x final %p\n", addr, *addr, *addr + regs[12]); |
| regs[11] = *addr + regs[12]; |
| loadstore(pc + 2, regs); |
| goto ret; |
| |
| undef: |
| doabort = 1; |
| |
| ret: |
| if (trace || doabort) { |
| printf(" %p coproc const %x %x %x\n", pc, *pc, *(pc+1), *(pc+2)); |
| } |
| if (doabort) |
| fabort(); |
| } |
| |
| |
| // returns number of words that the fp instruction is occupying, 0 if next instruction isn't float. |
| // TODO(kaib): insert sanity checks for coproc 1 |
| static uint32 |
| stepflt(uint32 *pc, uint32 *regs) |
| { |
| uint32 i, c; |
| |
| i = *pc; |
| c = i >> 25 & 7; |
| |
| switch(c) { |
| case 6: // 110 |
| loadstore(pc, regs); |
| return 1; |
| case 7: // 111 |
| if (i>>24 & 1) return 0; // ignore swi |
| |
| if (i>>4 & 1) { //data transfer |
| if ((i&0x00f0ff00) != 0x0090f100) { |
| printf(" %p %x\n", pc, i); |
| fabort(); |
| } |
| compare(pc, regs); |
| } else { |
| dataprocess(pc); |
| } |
| return 1; |
| } |
| |
| // lookahead for virtual instructions that span multiple arm instructions |
| c = ((*pc & 0x0f000000) >> 16) | |
| ((*(pc + 1) & 0x0f000000) >> 20) | |
| ((*(pc + 2) & 0x0f000000) >> 24); |
| if(c == 0x50d) { // 0101 0000 1101 |
| loadconst(pc, regs); |
| return 3; |
| } |
| |
| return 0; |
| } |
| |
| #pragma textflag 7 |
| uint32* |
| _sfloat2(uint32 *lr, uint32 r0) |
| { |
| uint32 skip; |
| uint32 cpsr; |
| |
| while(skip = stepflt(lr, &r0)) { |
| lr += skip; |
| } |
| return lr; |
| } |
| |