diff options
Diffstat (limited to 'sys/arch/luna88k/ddb/db_sstep.c')
-rw-r--r-- | sys/arch/luna88k/ddb/db_sstep.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/sys/arch/luna88k/ddb/db_sstep.c b/sys/arch/luna88k/ddb/db_sstep.c new file mode 100644 index 00000000000..7c9a2da3254 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_sstep.c @@ -0,0 +1,337 @@ +/* $OpenBSD: db_sstep.c,v 1.1 2004/04/21 15:23:51 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_access.h> /* db_get_value() */ +#include <ddb/db_break.h> /* db_breakpoint_t */ + +/* + * Support routines for software single step. + * + * Author: Daniel Stodolsky (danner@cs.cmu.edu) + * + */ + +boolean_t inst_delayed(unsigned int ins); + +#ifdef INTERNAL_SSTEP +db_breakpoint_t db_not_taken_bkpt = 0; +db_breakpoint_t db_taken_bkpt = 0; +#endif + +/* + * Returns TRUE is the instruction a branch or jump instruction + * (br, bb0, bb1, bcnd, jmp) but not a function call (bsr or jsr) + */ +boolean_t +inst_branch(ins) + unsigned int ins; +{ + /* check high five bits */ + switch (ins >> (32 - 5)) { + case 0x18: /* br */ + case 0x1a: /* bb0 */ + case 0x1b: /* bb1 */ + case 0x1d: /* bcnd */ + return TRUE; + break; + case 0x1e: /* could be jmp */ + if ((ins & 0xfffffbe0U) == 0xf400c000U) + return TRUE; + } + + return FALSE; +} + +/* + * inst_load(ins) + * Returns the number of words the instruction loads. byte, + * half and word count as 1; double word as 2 + */ +unsigned +inst_load(ins) + unsigned int ins; +{ + /* look at the top six bits, for starters */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + + case 0x2: /* unsigned half-word load imm */ + case 0x3: /* unsigned byte load imm */ + case 0x5: /* signed word load imm */ + case 0x6: /* signed half-word load imm */ + case 0x7: /* signed byte load imm */ + return 1; + + case 0x4: /* signed double word load imm */ + return 2; + + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + if ((ins & 0xf400c0e0U) == 0xf4000000U) /* is load/xmem */ + switch ((ins & 0x0000fce0) >> 5) { /* look at bits 15-5, but mask bits 8-9 */ + case 0x0: /* xmem byte */ + case 0x1: /* xmem word */ + case 0x2: /* unsigned half word */ + case 0x3: /* unsigned byte load */ + case 0x5: /* signed word load */ + case 0x6: /* signed half-word load */ + case 0x7: /* signed byte load */ + return 1; + + case 0x4: /* signed double word load */ + return 2; + } /* end switch load/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_store + * Like inst_load, except for store instructions. + */ +unsigned +inst_store(ins) + unsigned int ins; +{ + /* decode top 6 bits again */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word imm */ + case 0xa: /* store half-word imm */ + case 0xb: /* store byte imm */ + return 1; + + case 0x8: /* store double word */ + return 2; + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + /* check bits 15,14,12,7,6,5 are all 0 */ + if ((ins & 0x0000d0e0U) == 0) + switch ((ins & 0x00003c00U) >> 10) { /* decode bits 10-13 */ + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word */ + case 0xa: /* store half-word */ + case 0xb: /* store byte */ + return 1; + + case 0x8: /* store double word */ + return 2; + } /* end switch store/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_delayed + * Returns TRUE if this instruction is followed by a delay slot. + * Could be br.n, bsr.n bb0.n, bb1.n, bcnd.n or jmp.n or jsr.n + */ +boolean_t +inst_delayed(ins) + unsigned int ins; +{ + /* check the br, bsr, bb0, bb1, bcnd cases */ + switch ((ins & 0xfc000000U) >> (32 - 6)) { + case 0x31: /* br */ + case 0x33: /* bsr */ + case 0x35: /* bb0 */ + case 0x37: /* bb1 */ + case 0x3b: /* bcnd */ + return TRUE; + } + + /* check the jmp, jsr cases */ + /* mask out bits 0-4, bit 11 */ + return ((ins & 0xfffff7e0U) == 0xf400c400U) ? TRUE : FALSE; +} + +/* + * next_instr_address(pc,delay_slot,task) has the following semantics. + * Let inst be the instruction at pc. + * If delay_slot = 1, next_instr_address should return + * the address of the instruction in the delay slot; if this instruction + * does not have a delay slot, it should return pc. + * If delay_slot = 0, next_instr_address should return the + * address of next sequential instruction, or pc if the instruction is + * followed by a delay slot. + * + * 91-11-28 jfriedl: I think the above is wrong. I think it should be: + * if delay_slot true, return address of the delay slot if there is one, + * return pc otherwise. + * if delay_slot false, return (pc + 4) regardless. + * + */ +db_addr_t +next_instr_address(pc, delay_slot) + db_addr_t pc; + unsigned delay_slot; +{ + if (delay_slot == 0) + return pc + 4; + else { + if (inst_delayed(db_get_value(pc, sizeof(int), FALSE))) + return pc + 4; + else + return pc; + } +} + + +/* + * branch_taken(instruction, program counter, func, func_data) + * + * instruction will be a control flow instruction location at address pc. + * Branch taken is supposed to return the address to which the instruction + * would jump if the branch is taken. Func can be used to get the current + * register values when invoked with a register number and func_data as + * arguments. + * + * If the instruction is not a control flow instruction, panic. + */ +db_addr_t +branch_taken(inst, pc, func, func_data) + u_int inst; + db_addr_t pc; + db_expr_t (*func)(db_regs_t *, int); + db_regs_t *func_data; +{ + /* check if br/bsr */ + if ((inst & 0xf0000000U) == 0xc0000000U) { + /* signed 26 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x03ffffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x08000000U) + inst |= 0xf0000000U; + return pc + inst; + } + + /* check if bb0/bb1/bcnd case */ + switch ((inst & 0xf8000000U)) { + case 0xd0000000U: /* bb0 */ + case 0xd8000000U: /* bb1 */ + case 0xe8000000U: /* bcnd */ + /* signed 16 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x0000ffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x00020000U) + inst |= 0xfffc0000U; + return pc + inst; + } + + /* check jmp/jsr case */ + /* check bits 5-31, skipping 10 & 11 */ + if ((inst & 0xfffff3e0U) == 0xf400c000U) { + return (*func)(func_data, (inst & 0x0000001fU)); /* the register value */ + } + + + panic("branch_taken"); + return 0; /* keeps compiler happy */ +} + +/* + * getreg_val - handed a register number and an exception frame. + * Returns the value of the register in the specified + * frame. Only makes sense for general registers. + */ + +db_expr_t +getreg_val(frame, regno) + db_regs_t *frame; + int regno; +{ + if (regno == 0) + return 0; + else if (regno < 31) + return frame->r[regno]; + else + panic("bad register number (%d) to getreg_val.", regno); +} + +#ifdef INTERNAL_SSTEP +void +db_set_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr |= (PSR_TRACE | PSR_SER)); + } else { + db_addr_t pc = PC_REGS(regs); +#ifndef SOFTWARE_SSTEP_EMUL + db_addr_t brpc; + u_int inst; + + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + */ + inst = db_get_value(pc, sizeof(int), FALSE); + if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { + brpc = branch_taken(inst, pc, getreg_val, regs); + if (brpc != pc) { /* self-branches are hopeless */ + db_taken_bkpt = db_set_temp_breakpoint(brpc); + } +#if 0 + /* XXX this seems like a true bug, no? */ + pc = next_instr_address(pc, 1); +#endif + } +#endif /*SOFTWARE_SSTEP_EMUL*/ + pc = next_instr_address(pc, 0); + db_not_taken_bkpt = db_set_temp_breakpoint(pc); + } +} + +void +db_clear_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr &= ~(PSR_TRACE | PSR_SER)); + } else { + if (db_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_taken_bkpt); + db_taken_bkpt = 0; + } + if (db_not_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_not_taken_bkpt); + db_not_taken_bkpt = 0; + } + } +} +#endif |