--- trunk/src/cpus/cpu_sparc_instr.c 2007/10/08 16:20:26 28 +++ trunk/src/cpus/cpu_sparc_instr.c 2007/10/08 16:20:40 30 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_sparc_instr.c,v 1.21 2006/07/02 11:01:19 debug Exp $ + * $Id: cpu_sparc_instr.c,v 1.23 2006/07/24 22:32:44 debug Exp $ * * SPARC instructions. * @@ -106,6 +106,248 @@ /* + * bl + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(bl) +{ + MODE_uint_t old_pc = cpu->pc; + int n = (cpu->cd.sparc.ccr & SPARC_CCR_N) ? 1 : 0; + int v = (cpu->cd.sparc.ccr & SPARC_CCR_V) ? 1 : 0; + int cond = n ^ v; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} +X(bl_xcc) +{ + MODE_uint_t old_pc = cpu->pc; + int n = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_N)? 1:0; + int v = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_V)? 1:0; + int cond = n ^ v; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* + * bne + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(bne) +{ + MODE_uint_t old_pc = cpu->pc; + int cond = (cpu->cd.sparc.ccr & SPARC_CCR_Z) ? 0 : 1; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} +X(bne_a) +{ + MODE_uint_t old_pc = cpu->pc; + int cond = (cpu->cd.sparc.ccr & SPARC_CCR_Z) ? 0 : 1; + cpu->delay_slot = TO_BE_DELAYED; + if (!cond) { + /* Nullify the delay slot: */ + cpu->cd.sparc.next_ic ++; + return; + } + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* + * bg + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(bg) +{ + MODE_uint_t old_pc = cpu->pc; + int n = (cpu->cd.sparc.ccr & SPARC_CCR_N) ? 1 : 0; + int v = (cpu->cd.sparc.ccr & SPARC_CCR_V) ? 1 : 0; + int z = (cpu->cd.sparc.ccr & SPARC_CCR_Z) ? 1 : 0; + int cond = !(z | (n ^ v)); + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} +X(bg_xcc) +{ + MODE_uint_t old_pc = cpu->pc; + int n = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_N)? 1:0; + int v = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_V)? 1:0; + int z = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_Z)? 1:0; + int cond = !(z | (n ^ v)); + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* + * bge + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(bge) +{ + MODE_uint_t old_pc = cpu->pc; + int n = (cpu->cd.sparc.ccr & SPARC_CCR_N) ? 1 : 0; + int v = (cpu->cd.sparc.ccr & SPARC_CCR_V) ? 1 : 0; + int cond = !(n ^ v); + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} +X(bge_xcc) +{ + MODE_uint_t old_pc = cpu->pc; + int n = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_N)? 1:0; + int v = ((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_V)? 1:0; + int cond = !(n ^ v); + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* + * be + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(be) +{ + MODE_uint_t old_pc = cpu->pc; + int cond = cpu->cd.sparc.ccr & SPARC_CCR_Z; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} +X(be_xcc) +{ + MODE_uint_t old_pc = cpu->pc; + int cond = (cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) & SPARC_CCR_Z; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* * ba * * arg[0] = int32_t displacement compared to the start of the current page @@ -129,6 +371,33 @@ /* + * brnz + * + * arg[0] = int32_t displacement compared to the start of the current page + * arg[1] = ptr to rs1 + */ +X(brnz) +{ + MODE_uint_t old_pc = cpu->pc; + int cond = reg(ic->arg[1]) != 0; + cpu->delay_slot = TO_BE_DELAYED; + ic[1].f(cpu, ic+1); + cpu->n_translated_instrs ++; + if (!(cpu->delay_slot & EXCEPTION_IN_DELAY_SLOT)) { + /* Note: Must be non-delayed when jumping to the new pc: */ + cpu->delay_slot = NOT_DELAYED; + if (cond) { + old_pc &= ~((SPARC_IC_ENTRIES_PER_PAGE - 1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc = old_pc + (int32_t)ic->arg[0]; + quick_pc_to_pointers(cpu); + } + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* * Jump and link * * arg[0] = ptr to rs1 @@ -337,6 +606,23 @@ X(sra_imm) { reg(ic->arg[2]) = (int32_t)reg(ic->arg[0]) >> ic->arg[1]; } X(srax_imm) { reg(ic->arg[2]) = (int64_t)reg(ic->arg[0]) >> ic->arg[1]; } +X(udiv) +{ + uint64_t z = (cpu->cd.sparc.y << 32) | (uint32_t)reg(ic->arg[0]); + z /= (uint32_t)reg(ic->arg[1]); + if (z > 0xffffffff) + z = 0xffffffff; + reg(ic->arg[2]) = z; +} +X(udiv_imm) +{ + uint64_t z = (cpu->cd.sparc.y << 32) | (uint32_t)reg(ic->arg[0]); + z /= (uint32_t)ic->arg[1]; + if (z > 0xffffffff) + z = 0xffffffff; + reg(ic->arg[2]) = z; +} + /* * Save: @@ -385,6 +671,114 @@ /* + * Add with ccr update: + * + * arg[0] = ptr to rs1 + * arg[1] = ptr to rs2 or an immediate value (int32_t) + * arg[2] = ptr to rd + */ +int32_t sparc_addcc32(struct cpu *cpu, int32_t rs1, int32_t rs2); +#ifdef MODE32 +int32_t sparc_addcc32(struct cpu *cpu, int32_t rs1, int32_t rs2) +#else +int64_t sparc_addcc64(struct cpu *cpu, int64_t rs1, int64_t rs2) +#endif +{ + int cc = 0, sign1 = 0, sign2 = 0, signd = 0, mask = SPARC_CCR_ICC_MASK; + MODE_int_t rd = rs1 + rs2; + if (rd == 0) + cc = SPARC_CCR_Z; + else if (rd < 0) + cc = SPARC_CCR_N, signd = 1; + if (rs1 < 0) + sign1 = 1; + if (rs2 < 0) + sign2 = 1; + if (sign1 == sign2 && sign1 != signd) + cc |= SPARC_CCR_V; + /* TODO: SPARC_CCR_C */ +#ifndef MODE32 + mask <<= SPARC_CCR_XCC_SHIFT; + cc <<= SPARC_CCR_XCC_SHIFT; +#endif + cpu->cd.sparc.ccr &= ~mask; + cpu->cd.sparc.ccr |= cc; + return rd; +} +X(addcc) +{ + /* Like add, but updates the ccr, and does both 32-bit and + 64-bit comparison at the same time. */ + MODE_int_t rs1 = reg(ic->arg[0]), rs2 = reg(ic->arg[1]), rd; + rd = sparc_addcc32(cpu, rs1, rs2); +#ifndef MODE32 + rd = sparc_addcc64(cpu, rs1, rs2); +#endif + reg(ic->arg[2]) = rd; +} +X(addcc_imm) +{ + MODE_int_t rs1 = reg(ic->arg[0]), rs2 = (int32_t)ic->arg[1], rd; + rd = sparc_addcc32(cpu, rs1, rs2); +#ifndef MODE32 + rd = sparc_addcc64(cpu, rs1, rs2); +#endif + reg(ic->arg[2]) = rd; +} + + +/* + * And with ccr update: + * + * arg[0] = ptr to rs1 + * arg[1] = ptr to rs2 or an immediate value (int32_t) + * arg[2] = ptr to rd + */ +int32_t sparc_andcc32(struct cpu *cpu, int32_t rs1, int32_t rs2); +#ifdef MODE32 +int32_t sparc_andcc32(struct cpu *cpu, int32_t rs1, int32_t rs2) +#else +int64_t sparc_andcc64(struct cpu *cpu, int64_t rs1, int64_t rs2) +#endif +{ + int cc = 0, mask = SPARC_CCR_ICC_MASK; + MODE_int_t rd = rs1 & rs2; + if (rd == 0) + cc = SPARC_CCR_Z; + else if (rd < 0) + cc = SPARC_CCR_N; + /* Note: SPARC_CCR_C and SPARC_CCR_V are always zero. */ +#ifndef MODE32 + mask <<= SPARC_CCR_XCC_SHIFT; + cc <<= SPARC_CCR_XCC_SHIFT; +#endif + cpu->cd.sparc.ccr &= ~mask; + cpu->cd.sparc.ccr |= cc; + return rd; +} +X(andcc) +{ + /* Like and, but updates the ccr, and does both 32-bit and + 64-bit comparison at the same time. */ + MODE_int_t rs1 = reg(ic->arg[0]), rs2 = reg(ic->arg[1]), rd; + rd = sparc_andcc32(cpu, rs1, rs2); +#ifndef MODE32 + rd = sparc_andcc64(cpu, rs1, rs2); +#endif + reg(ic->arg[2]) = rd; +} +X(andcc_imm) +{ + MODE_int_t rs1 = reg(ic->arg[0]), rs2 = (int32_t)ic->arg[1], rd; + rd = sparc_andcc32(cpu, rs1, rs2); +#ifndef MODE32 + rd = sparc_andcc64(cpu, rs1, rs2); +#endif + reg(ic->arg[2]) = rd; +} + + +/* * Subtract with ccr update: * * arg[0] = ptr to rs1 @@ -445,7 +839,7 @@ /* - * rd: Read special register: + * rd: Read special register * * arg[2] = ptr to rd */ @@ -456,6 +850,21 @@ /* + * rdpr: Read privileged register + * + * arg[2] = ptr to rd + */ +X(rdpr_tba) +{ + reg(ic->arg[2]) = cpu->cd.sparc.tba; +} +X(rdpr_ver) +{ + reg(ic->arg[2]) = cpu->cd.sparc.ver; +} + + +/* * wrpr: Write to privileged register * * arg[0] = ptr to rs1 @@ -631,6 +1040,30 @@ case 0: switch (op2) { + case 1: /* branch (icc or xcc) */ + tmpi32 = (iword << 13); + tmpi32 >>= 11; + ic->arg[0] = (int32_t)tmpi32 + (addr & 0xffc); + /* rd contains the annul bit concatenated with 4 bits + of condition code. cc=0 for icc, 2 for xcc: */ + /* TODO: samepage */ + switch (rd + (cc << 5)) { + case 0x01: ic->f = instr(be); break; + case 0x03: ic->f = instr(bl); break; + case 0x09: ic->f = instr(bne); break; + case 0x0a: ic->f = instr(bg); break; + case 0x0b: ic->f = instr(bge); break; + case 0x19: ic->f = instr(bne_a); break; + case 0x41: ic->f = instr(be_xcc); break; + case 0x43: ic->f = instr(bl_xcc); break; + case 0x4a: ic->f = instr(bg_xcc); break; + case 0x4b: ic->f = instr(bge_xcc); break; + default:fatal("Unimplemented branch, 0x%x\n", + rd + (cc<<5)); + goto bad; + } + break; + case 2: /* branch (32-bit integer comparison) */ tmpi32 = (iword << 10); tmpi32 >>= 8; @@ -639,10 +1072,26 @@ of condition code: */ /* TODO: samepage */ switch (rd) { - case 0x08:/* ba */ - ic->f = instr(ba); - break; - default:goto bad; + case 0x01: ic->f = instr(be); break; + case 0x03: ic->f = instr(bl); break; + case 0x08: ic->f = instr(ba); break; + case 0x0b: ic->f = instr(bge); break; + default:fatal("Unimplemented branch rd=%i\n", rd); + goto bad; + } + break; + + case 3: /* branch on register, 64-bit integer comparison */ + tmpi32 = ((iword & 0x300000) >> 6) | (iword & 0x3fff); + tmpi32 <<= 16; + tmpi32 >>= 14; + ic->arg[0] = (int32_t)tmpi32 + (addr & 0xffc); + ic->arg[1] = (size_t)&cpu->cd.sparc.r[rs1]; + /* TODO: samepage */ + switch (btype) { + case 0x05: ic->f = instr(brnz); break; + default:fatal("Unimplemented branch 0x%x\n", rd); + goto bad; } break; @@ -678,6 +1127,9 @@ case 2: /* or */ case 3: /* xor */ case 4: /* sub */ + case 14:/* udiv */ + case 16:/* addcc */ + case 17:/* andcc */ case 20:/* subcc (cmp) */ case 37:/* sll */ case 38:/* srl */ @@ -693,6 +1145,9 @@ case 2: ic->f = instr(or_imm); break; case 3: ic->f = instr(xor_imm); break; case 4: ic->f = instr(sub_imm); break; + case 14:ic->f = instr(udiv_imm); break; + case 16:ic->f = instr(addcc_imm); break; + case 17:ic->f = instr(andcc_imm); break; case 20:ic->f = instr(subcc_imm); break; case 37:if (siconst & 0x1000) { ic->f = instr(sllx_imm); @@ -736,6 +1191,9 @@ case 2: ic->f = instr(or); break; case 3: ic->f = instr(xor); break; case 4: ic->f = instr(sub); break; + case 14:ic->f = instr(udiv); break; + case 16:ic->f = instr(addcc); break; + case 17:ic->f = instr(andcc); break; case 20:ic->f = instr(subcc); break; case 37:if (siconst & 0x1000) { ic->f = instr(sllx); @@ -768,8 +1226,14 @@ * Some opcodes should write to the scratch * register instead of becoming NOPs, when * rd is the zero register. + * + * Any opcode which updates the condition + * codes, or anything which changes register + * windows. */ switch (op2) { + case 16:/* addcc */ + case 17:/* andcc */ case 20:/* subcc */ case 60:/* save */ ic->arg[2] = (size_t) @@ -792,6 +1256,23 @@ goto bad; } break; + + case 42:/* rdpr on sparcv9 */ + if (cpu->is_32bit) { + fatal("opcode 2,42 not yet implemented" + " for 32-bit cpus\n"); + goto bad; + } + ic->arg[2] = (size_t)&cpu->cd.sparc.r[rd]; + if (rd == SPARC_ZEROREG) + ic->f = instr(nop); + switch (rs1) { + case 5: ic->f = instr(rdpr_tba); break; + case 31: ic->f = instr(rdpr_ver); break; + default:fatal("Unimplemented rs1=%i\n", rs1); + goto bad; + } + break; case 48:/* wr (Note: works as xor) */ ic->arg[0] = (size_t)&cpu->cd.sparc.r[rs1];