--- 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:58 32 @@ -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.25 2006/09/04 15:35:55 debug Exp $ * * SPARC instructions. * @@ -106,6 +106,299 @@ /* + * 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; +} + + +/* + * ble + * + * arg[0] = int32_t displacement compared to the start of the current page + */ +X(ble) +{ + 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 = (n ^ v) || 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(ble_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 = (n ^ v) || 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; +} + + +/* + * 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 +422,113 @@ /* + * 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; +} + + +/* + * Save: + * + * arg[0] = ptr to rs1 + * arg[1] = ptr to rs2 or an immediate value (int32_t) + * arg[2] = ptr to rd (_after_ the register window change) + */ +X(save_v9_imm) +{ + MODE_uint_t rs = reg(ic->arg[0]) + (int32_t)ic->arg[1]; + int cwp = cpu->cd.sparc.cwp; + + if (cpu->cd.sparc.cansave == 0) { + fatal("save_v9_imm: spill trap. TODO\n"); + exit(1); + } + + if (cpu->cd.sparc.cleanwin - cpu->cd.sparc.canrestore == 0) { + fatal("save_v9_imm: clean_window trap. TODO\n"); + exit(1); + } + + /* Save away old in registers: */ + memcpy(&cpu->cd.sparc.r_inout[cwp][0], &cpu->cd.sparc.r[SPARC_REG_I0], + sizeof(cpu->cd.sparc.r[SPARC_REG_I0]) * N_SPARC_INOUT_REG); + + /* Save away old local registers: */ + memcpy(&cpu->cd.sparc.r_local[cwp][0], &cpu->cd.sparc.r[SPARC_REG_L0], + sizeof(cpu->cd.sparc.r[SPARC_REG_L0]) * N_SPARC_INOUT_REG); + + cpu->cd.sparc.cwp = (cwp + 1) % cpu->cd.sparc.cpu_type.nwindows; + cpu->cd.sparc.cansave --; + cpu->cd.sparc.canrestore ++; /* TODO: modulo here too? */ + cwp = cpu->cd.sparc.cwp; + + /* The out registers become the new in registers: */ + memcpy(&cpu->cd.sparc.r[SPARC_REG_I0], &cpu->cd.sparc.r[SPARC_REG_O0], + sizeof(cpu->cd.sparc.r[SPARC_REG_O0]) * N_SPARC_INOUT_REG); + + /* Read new local registers: */ + memcpy(&cpu->cd.sparc.r[SPARC_REG_L0], &cpu->cd.sparc.r_local[cwp][0], + sizeof(cpu->cd.sparc.r[SPARC_REG_L0]) * N_SPARC_INOUT_REG); + + reg(ic->arg[2]) = rs; +} + + +/* + * Restore: + */ +X(restore) +{ + int cwp = cpu->cd.sparc.cwp; + + if (cpu->cd.sparc.canrestore == 0) { + fatal("restore: spill trap. TODO\n"); + exit(1); + } + + cpu->cd.sparc.cwp = cwp - 1; + if (cwp == 0) + cpu->cd.sparc.cwp = cpu->cd.sparc.cpu_type.nwindows - 1; + cpu->cd.sparc.cansave ++; + cpu->cd.sparc.canrestore --; + cwp = cpu->cd.sparc.cwp; + + /* The in registers become the new out registers: */ + memcpy(&cpu->cd.sparc.r[SPARC_REG_O0], &cpu->cd.sparc.r[SPARC_REG_I0], + sizeof(cpu->cd.sparc.r[SPARC_REG_O0]) * N_SPARC_INOUT_REG); + + /* Read back the local registers: */ + memcpy(&cpu->cd.sparc.r[SPARC_REG_L0], &cpu->cd.sparc.r_local[cwp][0], + sizeof(cpu->cd.sparc.r[SPARC_REG_L0]) * N_SPARC_INOUT_REG); + + /* Read back the in registers: */ + memcpy(&cpu->cd.sparc.r[SPARC_REG_I0], &cpu->cd.sparc.r_inout[cwp][0], + sizeof(cpu->cd.sparc.r[SPARC_REG_I0]) * N_SPARC_INOUT_REG); +} + + +/* * Jump and link * * arg[0] = ptr to rs1 @@ -287,6 +687,100 @@ /* + * Return + * + * arg[0] = ptr to rs1 + * arg[1] = ptr to rs2 or an immediate value (int32_t) + */ +X(return_imm) +{ + int low_pc = ((size_t)ic - (size_t)cpu->cd.sparc.cur_ic_page) + / sizeof(struct sparc_instr_call); + cpu->pc &= ~((SPARC_IC_ENTRIES_PER_PAGE-1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc += (low_pc << SPARC_INSTR_ALIGNMENT_SHIFT); + + 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; + cpu->pc = reg(ic->arg[0]) + (int32_t)ic->arg[1]; + quick_pc_to_pointers(cpu); + instr(restore)(cpu, ic); + } else + cpu->delay_slot = NOT_DELAYED; +} +X(return_reg) +{ + int low_pc = ((size_t)ic - (size_t)cpu->cd.sparc.cur_ic_page) + / sizeof(struct sparc_instr_call); + cpu->pc &= ~((SPARC_IC_ENTRIES_PER_PAGE-1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc += (low_pc << SPARC_INSTR_ALIGNMENT_SHIFT); + + 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; + cpu->pc = reg(ic->arg[0]) + reg(ic->arg[1]); + quick_pc_to_pointers(cpu); + instr(restore)(cpu, ic); + } else + cpu->delay_slot = NOT_DELAYED; +} +X(return_imm_trace) +{ + int low_pc = ((size_t)ic - (size_t)cpu->cd.sparc.cur_ic_page) + / sizeof(struct sparc_instr_call); + cpu->pc &= ~((SPARC_IC_ENTRIES_PER_PAGE-1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc += (low_pc << SPARC_INSTR_ALIGNMENT_SHIFT); + + 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; + cpu->pc = reg(ic->arg[0]) + (int32_t)ic->arg[1]; + cpu_functioncall_trace(cpu, cpu->pc); + quick_pc_to_pointers(cpu); + instr(restore)(cpu, ic); + } else + cpu->delay_slot = NOT_DELAYED; +} +X(return_reg_trace) +{ + int low_pc = ((size_t)ic - (size_t)cpu->cd.sparc.cur_ic_page) + / sizeof(struct sparc_instr_call); + cpu->pc &= ~((SPARC_IC_ENTRIES_PER_PAGE-1) + << SPARC_INSTR_ALIGNMENT_SHIFT); + cpu->pc += (low_pc << SPARC_INSTR_ALIGNMENT_SHIFT); + + 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; + cpu->pc = reg(ic->arg[0]) + reg(ic->arg[1]); + cpu_functioncall_trace(cpu, cpu->pc); + quick_pc_to_pointers(cpu); + instr(restore)(cpu, ic); + } else + cpu->delay_slot = NOT_DELAYED; +} + + +/* * set: Set a register to a value (e.g. sethi). * * arg[0] = ptr to rd @@ -309,6 +803,8 @@ X(add_imm) { reg(ic->arg[2]) = reg(ic->arg[0]) + (int32_t)ic->arg[1]; } X(and) { reg(ic->arg[2]) = reg(ic->arg[0]) & reg(ic->arg[1]); } X(and_imm) { reg(ic->arg[2]) = reg(ic->arg[0]) & (int32_t)ic->arg[1]; } +X(andn) { reg(ic->arg[2]) = reg(ic->arg[0]) & ~reg(ic->arg[1]); } +X(andn_imm) { reg(ic->arg[2]) = reg(ic->arg[0]) & ~(int32_t)ic->arg[1]; } X(or) { reg(ic->arg[2]) = reg(ic->arg[0]) | reg(ic->arg[1]); } X(or_imm) { reg(ic->arg[2]) = reg(ic->arg[0]) | (int32_t)ic->arg[1]; } X(xor) { reg(ic->arg[2]) = reg(ic->arg[0]) ^ reg(ic->arg[1]); } @@ -337,50 +833,129 @@ 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: + * 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 (_after_ the register window change) + * arg[2] = ptr to rd */ -X(save_v9_imm) +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 { - MODE_uint_t rs = reg(ic->arg[0]) + (int32_t)ic->arg[1]; - int cwp = cpu->cd.sparc.cwp; - - if (cpu->cd.sparc.cansave == 0) { - fatal("save_v9_imm: spill trap. TODO\n"); - exit(1); - } - - if (cpu->cd.sparc.cleanwin - cpu->cd.sparc.canrestore == 0) { - fatal("save_v9_imm: clean_window trap. TODO\n"); - exit(1); - } - - /* Save away old in registers: */ - memcpy(&cpu->cd.sparc.r_inout[cwp][0], &cpu->cd.sparc.r[SPARC_REG_I0], - sizeof(cpu->cd.sparc.r[SPARC_REG_I0]) * N_SPARC_INOUT_REG); - - /* Save away old local registers: */ - memcpy(&cpu->cd.sparc.r_local[cwp][0], &cpu->cd.sparc.r[SPARC_REG_L0], - sizeof(cpu->cd.sparc.r[SPARC_REG_L0]) * N_SPARC_INOUT_REG); - - cwp = cpu->cd.sparc.cwp = (cwp + 1) % cpu->cd.sparc.cpu_type.nwindows; - cpu->cd.sparc.cansave --; - cpu->cd.sparc.canrestore ++; /* TODO: modulo here too? */ - - /* The out registers become the new in registers: */ - memcpy(&cpu->cd.sparc.r[SPARC_REG_I0], &cpu->cd.sparc.r[SPARC_REG_O0], - sizeof(cpu->cd.sparc.r[SPARC_REG_O0]) * N_SPARC_INOUT_REG); + 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; +} - /* Read new local registers: */ - memcpy(&cpu->cd.sparc.r[SPARC_REG_L0], &cpu->cd.sparc.r_local[cwp][0], - sizeof(cpu->cd.sparc.r[SPARC_REG_L0]) * N_SPARC_INOUT_REG); - reg(ic->arg[2]) = rs; +/* + * 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; } @@ -445,7 +1020,7 @@ /* - * rd: Read special register: + * rd: Read special register * * arg[2] = ptr to rd */ @@ -456,6 +1031,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 +1221,34 @@ 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 0x02: ic->f = instr(ble); break; + case 0x03: ic->f = instr(bl); break; + case 0x08: ic->f = instr(ba); 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 0x42: ic->f = instr(ble_xcc);break; + case 0x43: ic->f = instr(bl_xcc); break; + case 0x48: ic->f = instr(ba); 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 +1257,27 @@ 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 0x09: ic->f = instr(bne); 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 +1313,10 @@ case 2: /* or */ case 3: /* xor */ case 4: /* sub */ + case 5: /* andn */ + case 14:/* udiv */ + case 16:/* addcc */ + case 17:/* andcc */ case 20:/* subcc (cmp) */ case 37:/* sll */ case 38:/* srl */ @@ -693,6 +1332,10 @@ 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 5: ic->f = instr(andn_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 +1379,10 @@ case 2: ic->f = instr(or); break; case 3: ic->f = instr(xor); break; case 4: ic->f = instr(sub); break; + case 5: ic->f = instr(andn); 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 +1415,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) @@ -793,6 +1446,23 @@ } 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]; if (use_imm) { @@ -877,6 +1547,26 @@ } break; + case 57:/* return */ + ic->arg[0] = (size_t)&cpu->cd.sparc.r[rs1]; + + if (use_imm) { + ic->arg[1] = siconst; + ic->f = instr(return_imm); + } else { + ic->arg[1] = (size_t)&cpu->cd.sparc.r[rs2]; + ic->f = instr(return_reg); + } + + /* special trace case: */ + if (cpu->machine->show_trace_tree) { + if (use_imm) + ic->f = instr(return_imm_trace); + else + ic->f = instr(return_reg_trace); + } + break; + default:fatal("TODO: unimplemented op2=%i for main " "opcode %i\n", op2, main_opcode); goto bad;