| /* Simulator breakpoint support. |
| Copyright (C) 1997 Free Software Foundation, Inc. |
| Contributed by Cygnus Support. |
| |
| This file is part of GDB, the GNU debugger. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along |
| with this program; if not, write to the Free Software Foundation, Inc., |
| 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| #include <stdio.h> |
| |
| #include "sim-main.h" |
| #include "sim-assert.h" |
| #include "sim-break.h" |
| |
| #ifndef SIM_BREAKPOINT |
| #define SIM_BREAKPOINT {0x00} |
| #define SIM_BREAKPOINT_SIZE (1) |
| #endif |
| |
| struct |
| sim_breakpoint |
| { |
| struct sim_breakpoint *next; |
| SIM_ADDR addr; /* Address of this breakpoint */ |
| int flags; |
| unsigned char loc_contents[SIM_BREAKPOINT_SIZE]; /* Contents of addr while |
| BP is enabled */ |
| }; |
| |
| #define SIM_BREAK_INSERTED 0x1 /* Breakpoint has been inserted */ |
| #define SIM_BREAK_DISABLED 0x2 /* Breakpoint is disabled */ |
| |
| static unsigned char sim_breakpoint [] = SIM_BREAKPOINT; |
| |
| static void insert_breakpoint PARAMS ((SIM_DESC sd, |
| struct sim_breakpoint *bp)); |
| static void remove_breakpoint PARAMS ((SIM_DESC sd, |
| struct sim_breakpoint *bp)); |
| static SIM_RC resume_handler PARAMS ((SIM_DESC sd)); |
| static SIM_RC suspend_handler PARAMS ((SIM_DESC sd)); |
| |
| |
| /* Do the actual work of inserting a breakpoint into the instruction |
| stream. */ |
| |
| static void |
| insert_breakpoint (sd, bp) |
| SIM_DESC sd; |
| struct sim_breakpoint *bp; |
| { |
| if (bp->flags & (SIM_BREAK_INSERTED | SIM_BREAK_DISABLED)) |
| return; |
| |
| sim_core_read_buffer (sd, NULL, exec_map, bp->loc_contents, |
| bp->addr, SIM_BREAKPOINT_SIZE); |
| sim_core_write_buffer (sd, NULL, exec_map, sim_breakpoint, |
| bp->addr, SIM_BREAKPOINT_SIZE); |
| bp->flags |= SIM_BREAK_INSERTED; |
| } |
| |
| /* Do the actual work of removing a breakpoint. */ |
| |
| static void |
| remove_breakpoint (sd, bp) |
| SIM_DESC sd; |
| struct sim_breakpoint *bp; |
| { |
| if (!(bp->flags & SIM_BREAK_INSERTED)) |
| return; |
| |
| sim_core_write_buffer (sd, NULL, exec_map, bp->loc_contents, |
| bp->addr, SIM_BREAKPOINT_SIZE); |
| bp->flags &= ~SIM_BREAK_INSERTED; |
| } |
| |
| /* Come here when a breakpoint insn is hit. If it's really a breakpoint, we |
| halt things, and never return. If it's a false hit, we return to let the |
| caller handle things. */ |
| |
| void |
| sim_handle_breakpoint (sd, cpu, cia) |
| SIM_DESC sd; |
| sim_cpu *cpu; |
| sim_cia cia; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| if (bp->addr == CIA_ADDR (cia)) |
| break; |
| |
| if (!bp || !(bp->flags & SIM_BREAK_INSERTED)) |
| return; |
| |
| sim_engine_halt (sd, STATE_CPU (sd, 0), NULL, cia, sim_stopped, SIM_SIGTRAP); |
| } |
| |
| /* Handler functions for simulator resume and suspend events. */ |
| |
| static SIM_RC |
| resume_handler (sd) |
| SIM_DESC sd; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| insert_breakpoint (sd, bp); |
| |
| return SIM_RC_OK; |
| } |
| |
| static SIM_RC |
| suspend_handler (sd) |
| SIM_DESC sd; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| remove_breakpoint (sd, bp); |
| |
| return SIM_RC_OK; |
| } |
| |
| /* Called from simulator module initialization. */ |
| |
| SIM_RC |
| sim_break_install (sd) |
| SIM_DESC sd; |
| { |
| sim_module_add_resume_fn (sd, resume_handler); |
| sim_module_add_suspend_fn (sd, suspend_handler); |
| |
| return SIM_RC_OK; |
| } |
| |
| /* Install a breakpoint. This is a user-function. The breakpoint isn't |
| actually installed here. We just record it. Resume_handler does the |
| actual work. |
| */ |
| |
| SIM_RC |
| sim_set_breakpoint (sd, addr) |
| SIM_DESC sd; |
| SIM_ADDR addr; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| if (bp->addr == addr) |
| return SIM_RC_DUPLICATE_BREAKPOINT; /* Already there */ |
| else |
| break; /* FIXME: why not scan all bp's? */ |
| |
| bp = ZALLOC (struct sim_breakpoint); |
| |
| bp->addr = addr; |
| bp->next = STATE_BREAKPOINTS (sd); |
| bp->flags = 0; |
| STATE_BREAKPOINTS (sd) = bp; |
| |
| return SIM_RC_OK; |
| } |
| |
| /* Delete a breakpoint. All knowlege of the breakpoint is removed from the |
| simulator. |
| */ |
| |
| SIM_RC |
| sim_clear_breakpoint (sd, addr) |
| SIM_DESC sd; |
| SIM_ADDR addr; |
| { |
| struct sim_breakpoint *bp, *bpprev; |
| |
| for (bp = STATE_BREAKPOINTS (sd), bpprev = NULL; |
| bp; |
| bpprev = bp, bp = bp->next) |
| if (bp->addr == addr) |
| break; |
| |
| if (!bp) |
| return SIM_RC_UNKNOWN_BREAKPOINT; |
| |
| remove_breakpoint (sd, bp); |
| |
| if (bpprev) |
| bpprev->next = bp->next; |
| else |
| STATE_BREAKPOINTS (sd) = NULL; |
| |
| zfree (bp); |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_clear_all_breakpoints (sd) |
| SIM_DESC sd; |
| { |
| while (STATE_BREAKPOINTS (sd)) |
| sim_clear_breakpoint (sd, STATE_BREAKPOINTS (sd)->addr); |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_enable_breakpoint (sd, addr) |
| SIM_DESC sd; |
| SIM_ADDR addr; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| if (bp->addr == addr) |
| break; |
| |
| if (!bp) |
| return SIM_RC_UNKNOWN_BREAKPOINT; |
| |
| bp->flags &= ~SIM_BREAK_DISABLED; |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_disable_breakpoint (sd, addr) |
| SIM_DESC sd; |
| SIM_ADDR addr; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| if (bp->addr == addr) |
| break; |
| |
| if (!bp) |
| return SIM_RC_UNKNOWN_BREAKPOINT; |
| |
| bp->flags |= SIM_BREAK_DISABLED; |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_enable_all_breakpoints (sd) |
| SIM_DESC sd; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| bp->flags &= ~SIM_BREAK_DISABLED; |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_disable_all_breakpoints (sd) |
| SIM_DESC sd; |
| { |
| struct sim_breakpoint *bp; |
| |
| for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) |
| bp->flags |= SIM_BREAK_DISABLED; |
| |
| return SIM_RC_OK; |
| } |