\ ========== Copyright Header Begin ==========================================
\ Hypervisor Software File: breakpt.fth
\ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
\ - Do no alter or remove copyright notices
\ - Redistribution and use of this software in source and binary forms, with
\ or without modification, are permitted provided that the following
\ - Redistribution of source code must retain the above copyright notice,
\ this list of conditions and the following disclaimer.
\ - Redistribution in binary form must reproduce the above copyright notice,
\ this list of conditions and the following disclaimer in the
\ documentation and/or other materials provided with the distribution.
\ Neither the name of Sun Microsystems, Inc. or the names of contributors
\ may be used to endorse or promote products derived from this software
\ without specific prior written permission.
\ This software is provided "AS IS," without a warranty of any kind.
\ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
\ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
\ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
\ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
\ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
\ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
\ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
\ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
\ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
\ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
\ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
\ You acknowledge that this software is not designed, licensed or
\ intended for use in the design, construction, operation or maintenance of
\ ========== Copyright Header End ============================================
\ breakpt.fth 2.15 01/05/18
\ Copyright 1985-1990 Bradley Forthware
\ Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved
\ Assembly language breakpoints
\ objects.fth Defining words for multiple code field words
\ registers.fth Defines the register save area.
\ catchexc.fth Saves the machine state in the register save area.
\ CPU & operating system dependent
\ machdep.fth Defines CPU-dependent words for placing breakpoints
\ and finding the next instruction.
\ breakpt.fth (This file) Manages the list of breakpoints, handles
\ single-stepping. Machine-independent
only forth also hidden also
\ nuser restartable? restartable? off
defer restart-step ( -- )
20 constant max#breakpoints
max#breakpoints array >breakpoint
max#breakpoints array >breakpoint-action
max#breakpoints array >saved-opcode
2 array >step-saved-opcode
variable pc-at-breakpoint
variable breakpoints-installed
: init-breakpoints ( -- )
breakpoints-installed off
\ Search the breakpoint table to see if adr is breakpointed.
\ If it is, return the index into the table, or -1 if it's not there.
: find-breakpoint ( adr -- breakpoint#|-1 )
loop ( breakpoint# | -1 )
\ Enter a breakpoint at addr. If adr is already breakpointed,
: set-breakpoint ( adr -- )
dup find-breakpoint ( adr breakpoint# )
#breakpoints @ max#breakpoints >= abort" Too many breakpoints"
#breakpoints @ 1 #breakpoints +! ( adr breakpoint# )
\ Set default action to be .breakpoint
0 swap find-breakpoint >breakpoint-action !
\ Display the breakpoint table.
: show-breakpoints ( -- )
i >breakpoint-action @ ?dup if ." { " >name .id ." } " then
\ If the breakpoint is installed in memory, take it out.
: repair-breakpoint ( breakpoint# -- )
dup >breakpoint @ at-breakpoint?
if dup >saved-opcode @ over >breakpoint @ op! then
\ Remove the breakpoint at adr from the table, if it's there.
: remove-breakpoint ( adr -- )
find-breakpoint ( breakpoint# )
dup 0< ( breakpoint# flag )
\ Shuffle the remaining breakpoints down to fill the vacated slot
#breakpoints @ swap 1+ ( last-breakpoint# breakpoint# )
i >breakpoint @ i 1- >breakpoint !
i >breakpoint-action @ i 1- >breakpoint-action !
\ When we restart the program, we have to put breakpoints at all the
\ places in the breakpoint list. If there is a breakpoint at the
\ current PC, we have to temporarily not put one there, because we
\ want to execute it at least once (presumably we just hit it).
\ So we have to single step by putting breakpoints at the next instruction,
\ then when we hit that instruction, we put the breakpoint at the previous
\ place. In fact, the "next instruction" may actually be 2 instructions
\ because the current instruction could be a branch.
: install-breakpoints ( -- )
breakpoints-installed @ if exit then
i >breakpoint @ ( breakpoint-adr )
over at-breakpoint? 0= if ( adr opcode )
i >saved-opcode ! ( breakpoint-adr )
: repair-breakpoints ( -- )
#breakpoints @ 0 ?do i repair-breakpoint loop
breakpoints-installed off
\ To single step, we have to breakpoint the instruction just after the
\ current instruction. If that instruction is a conditional branch, we
\ have to breakpoint both the next instruction and the branch target.
\ The machine-dependent next-instruction routine finds the next instruction
: set-step-breakpoints ( -- )
following-jsrs? @ next-instruction ( next-adr branch-target|0 )
swap ( step-breakpoint-adr0 step-breakpoint-adr1 )
dup i >step-breakpoint ! ( step-breakpoint-adr )
?dup if ( step-breakpoint-adr )
dup op@ i >step-saved-opcode ! ( step-breakpoint-adr )
: repair-step-breakpoints ( -- )
i >step-breakpoint @ ?dup if ( step-breakpoint-adr )
if i >step-saved-opcode @ i >step-breakpoint @ op! then
: remove-all-breakpoints ( -- )
repair-breakpoints repair-step-breakpoints #breakpoints off
: uninstall-breakpoints ( -- )
breakpoints-installed @ if
: current-address-breakpointed? ( -- flag )
: current-address-stepped? ( -- flag )
rpc 0 >step-breakpoint @ =
rpc 1 >step-breakpoint @ = or
: ?restart-ok ( -- ) restartable? @ 0= abort" No program is active." ;
: (step ( -- ) set-step-breakpoints ?restart-ok restart-step ;
\ Put stuff to do before returning to a client in this chain.
defer go-hook ' go-chain is go-hook
\ : breakpoint-go ( -- ) install-breakpoints restart ;
: steps ( n -- ) #steps ! following-jsrs? on (step ;
: hops ( n -- ) #steps ! following-jsrs? off (step ;
go-hook ?restart-ok #steps off
current-address-breakpointed?
if following-jsrs? on (step else install-breakpoints restart then
." Invalid breakpoint address " .x cr
swap find-breakpoint ( acf bp# | -1 )
: till ( adr -- ) +bp go ;
: return ( -- ) return-adr till ; \ Finish and return from subroutine
: returnl ( -- ) leaf-return-adr till ; \ Finish and ret. from leaf subr.
: finish-loop ( -- ) loop-exit-adr till ; \ Finish the enclosing loop
: gos ( n -- ) 1- #gos ! go ;
' .instruction is .breakpoint
: breakpoint-message ( -- )
\ If the trap type is inconsistent with a breakpoint, then we
\ just print the exception type and exit.
breakpoint-trap? 0= if .exception quit then \ Exit to interpreter
\ If we are doing multiple single-steps, then we decrement the
\ step count and continue stepping until the count reaches 0.
-1 #steps +! #steps @ if (step then \ Exit to program
quit \ Exit to interpreter
\ If we are at a single-step location, but the step count variable was 0,
\ then it was a "hidden step". A "hidden step" happens when "go" is
\ executed from a location where there is a breakpoint set. We had to
\ step once to execute the breakpointed instruction, and then we replace
\ the location with a breakpoint insruction and go.
pc-at-step @ if restartable? on go then \ Exit to program
\ If we are at a breakpoint location, then we consult the #gos variable
\ to determine how many more times to go, and either go or "quit" to
\ the interactive interpreter.
rpc find-breakpoint >breakpoint-action @ ?dup if
#gos @ if -1 #gos +! go then \ Exit to program
quit \ Exit to interpreter
\ If we get here, a "breakpoint trap" occurred at a location where
\ we don't think there should have been a breakpoint. This means
\ that the location happens to contain an instruction that causes the
\ same kind of trap that is used for breakpoints (whatever that is for
\ the particular system). This could happen if a previous breakpoint
\ didn't get cleaned up properly, or if memory got overwritten with
\ breakpoint (or equivalent) instructions, or if the program jumped to
\ an invalid location that happened to contain breakpoint (or equivalent)
.exception quit \ Exit to interpreter
: (handle-breakpoint ( -- )
current-address-stepped? pc-at-step !
current-address-breakpointed? pc-at-breakpoint !
' (handle-breakpoint is handle-breakpoint
\ Remove most-recently-set breakpoint
#breakpoints @ 1- repair-breakpoint
: .bp ( -- ) show-breakpoints ;
: bpoff ( -- ) remove-all-breakpoints ;
: skip ( -- ) bumppc go ;
chain: init ( -- ) init-breakpoints ;
also keys-forth definitions
only forth also definitions