/****************************************************************************
**
**  Name:  bkptexec.c
**
**  Description:
**     Breakpoint and Execution Control Server
**
**     This server handles source and assembly level control of emulation
**     and breakpoints.
**
**  Status:  TESTED
**
**  $Log:   S:/tbird/mt2_186/bkptexec/bkptexec.c_v  $
** 
**    Rev 1.7   03 Mar 1998 10:13:28   Eric
** Fixed bug in show all breakpoint window.
** 
**    Rev 1.7   02 Feb 1998 10:53:14   Eric
** Fixed a bug that can not enable after set breakpoint by Set Breakpoint
** Dialog window.
** 
**    Rev 1.6   05 Dec 1997 14:26:36   Winky
** 
**    Rev 1.5   26 Aug 1997 10:49:16   Judy
 * 
 *    Rev 1.0   26 Aug 1997 09:56:02   unknown
 * Initial revision.
** 
**    Rev 1.4   03 Jun 1997 14:24:42   Judy
** 
**    Rev 1.3   10 Apr 1997 08:33:56   Judy
** No change.
** 
**    Rev 1.2   04 Feb 1997 18:20:48   Judy
** 
**    Rev 1.1   13 Jan 1997 10:41:32   Judy
** fixed a bug that can not set HW breakpoint after doing go_into_call 
** 
**    Rev 1.0   16 Dec 1996 14:51:38   Judy
** Initial revision.
** 
**    Rev 1.82   21 Oct 1994 18:30:56   nghia
** Revised BxSetSrcBkptFromCli to check for valid code address after looking
** up for symbol information.  When symbols are protected mode symbols and
** their bases are not yet updated, symbols look-up using linear address can
** produce invalid symbol matching.  Also, revised bkptTable to skip over
** allocate modRefName if module does not have any reference information.
** Added check for valid bkptAddr before destroy the address.
** 
**    Rev 1.81   21 Oct 1994 09:02:24   nghia
** Fixed event notification (Pmode) for reset while the processor is halted.
** 
**    Rev 1.80   13 Oct 1994 13:13:32   nghia
** Fixed PPR 9775.  enumRunningSpinFlag does not reset if error occurred in
** the BxLLStartEmulation() routine.
** 
**    Rev 1.79   02 Aug 1994 18:33:46   nghia
** Revised internal breakpoint setting to skip extracting symbol information to
** speed up the step.
** Fixed bug in GoUntil routine that destroy address descriptor that already
** destroyed.
** Revised the while(#) with it nested number for debugging purposes.
** 
**    Rev 1.78   28 Jul 1994 11:53:24   nghia
** Fixed bug in set bkpt for ASM module that have line number without function
** block.
** Fixed classic Windows DLL bug - Callback function must have EXPORT.
** 
**    Rev 1.77   22 Jul 1994 14:28:26   john
** Added changes made in 1.72.1.1
** 
**    Rev 1.76   12 Jul 1994 11:25:16   ernie
** Changed firmware protocol for single-step.  The firmware now writes
** to the SD_EMULATION_RESULT member only once, when the command is
** completely done.  This allows the bkpt server to check for success
** without a race condition.
** 
**    Rev 1.75   17 Jun 1994 15:30:22   dennis
** Changed all breakpoint address descriptors to be linear.
** 
**    Rev 1.74   10 Jun 1994 11:25:20   dennis
** Fix a bug where the breakpoints were not being removed when a program was
** loaded without symbols.
** 
**    Rev 1.73   09 Jun 1994 13:41:56   dennis
** Changed call from AdrGetAddrOffset to AdrGetAddrPhysicalOffset. This will
** now use the segment and offset to calculate the physical offset.
** 
**    Rev 1.72   06 Apr 1994 16:46:24   nghia
** Fixed bug in breakpoint address:
** - Cannot use the AdrCreateAddress() to create new address for breakpoint.
** Use the AdrDuplicateAddress() to ensure that the address information such
** as Space, Type, etc. will be preserved for the new address descriptor.
** - Replaced the AdrCompareAddresses() to use the AdrComparePhysicalAddress().
** - Ignored the error code returns for wrong space, type, etc for the
** breakpoint address comparison.  If the two address are not in the same space,
** segment, or type; they're not equal, continue the comparison with the next
** one.
** - Also, use the current PC address descriptor for temporary breakpoint.
** 
**    Rev 1.71   22 Nov 1993 15:01:50   tom
** 9130: Clean up after breakpoints which exceed hardware limit.
** 
**    Rev 1.70   29 Oct 1993 14:12:08   tom
** 8972: Clear breakpoints when the ER_TOO_MANY_XBRKS condition is detected.
** 
**    Rev 1.69   07 Oct 1993 12:37:42   ernie
** Added support for stepMask
** 
**    Rev 1.68   22 Sep 1993 17:10:26   nghia
** Filter ER_NO_LINENUMS_ADDED to support set breakpoint in ASM module.
** 
**    Rev 1.67   23 Jul 1993 15:26:20   ernie
** 1. SrcStepFast() now sets dasm address before calling.
** 2. Changed algorithm for SrcStepFast so that step into a statement
**    containing a call thru a function pointer works correctly--stops
**    inside the function if there is source available, otherwise doesn't.
** 
**    Rev 1.66   20 Jul 1993 07:40:18   ernie
** Fixed a problem with source step when stepping out of a range with
** source into a range with no source.  A 'cannot find symbol' error was
** appearing because no source range could be found for the given address.
** 
**    Rev 1.65   16 Jul 1993 11:45:44   ernie
** Removed error.h.  All errors now in errcodec.h
** 
**    Rev 1.64   25 Jun 1993 16:35:18   paul
** Replace CHECK_ABORT macro with TskCheckAbort call
**
**    Rev 1.63   09 Jun 1993 09:05:48   ernie
** Performance enhancements:
** 1. Eliminated EVENT_BX_LL_* events and moved processing for those
**    events directly into BxEmulStateChange().
** 2. Rearchitected SrcStep() to use fewer emulation sessions and use
**    breakpoints rather than repeated steps.  This allows even lines
**    with loops or multiple calls to libraries to be stepped quickly.
**
**    Rev 1.62   25 May 1993 11:57:10   ernie
** 1. Moved read of systemType to BxInit() to save repeated calls.
** 2. BxLLStartEmulation now turns trace on only for free-run emulation.
** 3. Changed interface to DasmSetDasmSym().
**
**    Rev 1.61   13 May 1993 14:05:08   nghia
** Added AddrHasNoBkpt() to detect setting multiple breakpoints at the same
** location. - Breakpoint Server PIP for ver. 2.0
** Revised BxSetSrcBkpt() and BxSetBkpt().
**
**    Rev 1.60   05 May 1993 09:17:00   doug
** merge dummy (no hardware) into run-time version
**
**    Rev 1.59   01 Apr 1993 09:05:10   doug
** Use run-time system type to make bkptexec.dll generic for PowerPack and
** PowerScope.
**
**    Rev 1.58   30 Mar 1993 08:33:32   ernie
** Changed to not send halted event if single-step fails.  This could
** happen if the single step encountered a bus hang condition.
**
**    Rev 1.57   22 Mar 1993 07:53:48   ernie
** Changed error reporting mechanism from firmware for emulation commands.
** Now the SD_GO, etc. members set SD_EMULATION_RESULT to their return code
** rather than using the error code field of SD_EMULATION_STATE.  This
** prevents problems with others using the value of SD_EMULATION_STATE
** after an error occurs.
**
**    Rev 1.56   13 Jan 1993 13:39:22   john
** added compile time switch for ps vs. pp
**
**    Rev 1.55   12 Jan 1993 10:32:08   brucea
** Changed: system variable name BKPTCAUSE to BREAKCAUSE
** Added: at end of BxLLAsmStepInto code to update the bkptCauseSystemVar since
**    this code is the only place where step status is known.
**
**    Rev 1.54   18 Dec 1992 18:26:46   brucea
** Added: checks for moduleDesc != NULL_SYMBOL before calling
**    BxInsertBkptNamesAndOffset.
**
**    Rev 1.53   16 Dec 1992 07:16:50   brucea
** Added: init of firmware bkpts from BxInit
** Added: bkptCause and emulating system variables that report to CLI when a
**    change in their state occurs
** Fixed: bug in EVENT_BKPT_EDIT reporting when temp internal bkpt removed
**
**    Rev 1.52   08 Dec 1992 06:51:32   bruce
** Changed: BxGetBkptCount from BxAnyBkptsSet which now returns number of
**    current bkpts set, minus any internal temporaries
** Removed: old runaccess functions (now in bkroot)
** Modified: BxSrcStep algorithm to stop only when on the first address of
**    a new source range.
**
**    Rev 1.51   03 Dec 1992 08:13:06   brucea
** Added: globalEscPushed var to hold state of error in BxCallback
** Removed: static init flag bxInitialized in BxInit, put their originally so
**    function wouldn't be called twice; should have been fixed in Actor code
** Added: globalEscPushed to callbacks in BxCallback
** Changed: all bkpt setting functions to check BkProcessorMustBeHaltedAbsolute
**    rather than the one that is bypassed if runaccess is on
** Added: code to allow source browser to set a temporary internal bkpt for
**    goto cursor; this changed code in a number of functions
** Added: BxGoIntoCallReturn the check that code is presently on a JSR or RTS
**    which single steps the processor instead of setting bkpts.  It then
**    does same post process as if it hit a bkpt
** Added: bkptWasSet parameter to BxGoIntoCallReturnPostBkpt
** Added: to BxLLStartEmulation, check that emulator not already running;
**    a semaphore check in case user clicked on GO several times;
** Added: turning on hour glass in BxLLStartEmulation and reset and go functions
**    since it wasn't being set early enough before
** Added: PeekMessage loop inside BxWaitForEmulationHalt
**
**
**    Rev 1.50   10 Nov 1992 07:35:44   brucea
** Removed: steppingFlag clear and set in BxSrcStep; it left the flag in the
**    TRUE
**    state which caused subsequent go until's to hang
**
**    Rev 1.49   06 Nov 1992 07:16:54   brucea
** Removed debug code and vars
**
**    Rev 1.48   04 Nov 1992 07:12:18   brucea
** Added: check for address not found when making SymMapAddr2Symbol call in
**   case address passed in is not in any symbol range
** Added: turning off then on steppingFlag around start of emulation in BxSrcStep
** Turned off tracing when single stepping
**
**    Rev 1.47   22 Oct 1992 18:33:02   brucea
** Changed: BxLocalGoUntilCallReturn to step over any subroutine calls which have
**    no function symbol associated with it.  This keeps the source step
**    function at the source level and does not drop to asm level
**
**    Rev 1.46   16 Oct 1992 18:14:44   brucea
** Remove: reporting of bkpt change if it is temporary internal
** Check: when removing a bkpt, only re-program if it is not disabled since
**    it has already been removed from programmed list if disabled
** Changed: BxSrcStep to step over functions with no source even if in step
**    into mode
**
**    Rev 1.45   15 Oct 1992 18:31:48   brucea
** Fixed: bug in BxCallback in case EVENT_BKPT_HALTED where code was freeing
**    buffer that didn't exist if error occurred
** Fixed: bug which programmed bkpts when the breakpoint was initially defined
**    as disabled.
** Fixed: bug in BxLLAsmStepInto in which steppingFlag was not turned off if
**    an error occurred
** Removed: ESC check inside single step.  Now depends on SdWriteCmdReadResponse
**    to time out
**
**    Rev 1.44   18 Sep 1992 09:40:22   brucea
** Turned off debugging
** Added: BxHeapDump to debug memory leakages
** Fixed bug in BxSetSrcBkpt, BxSetSrcBkptFromCli where access to
**    .statementTextPtr memory was being made before it was allocated.
** Fixed memory leakage in BxLocalGoUntilCallReturn (was creating linenumAddrDesc
**    twice)
** Fixed memory leakage in BxGoIntoCallReturnPostBkpt
**
**
**    Rev 1.43   14 Sep 1992 17:30:48   brucea
** Changed: interface to BxRemoveMatchingTemporary to add boolean return param
** Changed: event generation to always generate EVENT_BKPT_EDIT (caused if a
**    temporary bkpt was found) after EVENT_BKPT_HALTED
** Added: BxResetAndStartEmulation to support firmware GR command
**
**    Rev 1.42   08 Sep 1992 13:03:14   brucea
** Renamed events
** Removed bkpt_changed event from BxRemoveMatchingTemporary
** Changed: event generation after programming bkpt; if error occurs from
**    programming, event not generated
**
**    Rev 1.41   03 Sep 1992 13:06:16   brucea
** Added: cleanup goto code to BxSetBkpt
**           "              to BxSetBkptWithIndex
** Fixed code to remove storage leaks
**
**    Rev 1.40   28 Aug 1992 21:07:28   brucea
** Created: BxRemoveBkptAllocation which free's alloc'ed memory of a bkpt
**    Added calls to this function for the two bkpt remove functions
**
**    Rev 1.39   27 Aug 1992 14:50:14   brucea
** Changed: BX_EMULATION_STATE and BX_CAUSE to use those defined in sdtempl.h
** Fixed (Doug did): bug in asm step over in which it did not return from
**    starting emulation
** Changed: BxLLStartEmulation to use SdWriteCmdReadResponse for starting emu
**    instead of polling to see if started.  Bug was that sometimes state
**    changed so quickly that by the time it was read, the value was EM_HALTED.
**
**    Rev 1.38   25 Aug 1992 10:52:48   brucea
** Removed: BxCheckForValidAddress and "moved" it to AdrIsValidCodeAddress
**
**    Rev 1.37   24 Aug 1992 16:34:40   ernie
** Fixed pointer indirection problem with BxGetCauseOfLastBreak
**
**    Rev 1.36   18 Aug 1992 19:23:52   brucea
** Changed: AdrConvAddressToTextNoFill to ...WithParams
**
**    Rev 1.35   18 Aug 1992 18:38:58   brucea
** Added: implementation of BxGetCauseOfLastBreak
**
**    Rev 1.34   13 Aug 1992 11:36:08   brucea
** BxSrcStep changed: Checking input params; moved in-range test to beginning of
**    while loop - this fixes step over bug;
** Cleaned up: BxGoUntilCallReturn, BxGoIntoCallReturn for error returns for
**    ESC
** Generate error if ASM_GRAN is the granularity state of
**    BxGoIntoCallReturnPostBkpt
** Removed code which turns off trace, since HW does it automatically
**
**
**    Rev 1.33   12 Aug 1992 09:52:02   brucea
** Added: BX_GO_STEP type to BX_GO_TYPE enum and to callback case for low level
**    halted event
** Added: BxMLStartEmulation which provides parameter to set the go state
** Generate _EMUL_STARTED for src step and _EMUL_HALTED when complete
**
**
**    Rev 1.32   10 Aug 1992 08:50:40   brucea
** Added: functions to implement step over in asm; includes BxLLAsmStepInto,
**    BxLLAsmStepOver, and exported BxAsmStepIntoOrOver
**
**    Rev 1.30   31 Jul 1992 18:36:10   brucea
** Renamed: BxCheckMatchingTemporary to BxRemoveMatchingTemporary
** Added: BxLLHaltEmulation
** Added: registration on HL event to print out next execution line when
**    processor halted (in CLI)
** Added: BxRemoveMatchingTemporary upon receipt of emulator halted (low level)
**
**    Rev 1.29   31 Jul 1992 09:53:10   mindy
** a) Added a static for BxInit to use to assure that initialization is only
**    done once regardless of how many times BxInit is called.  In next release
**    hopefully Actor won't be trying to initialize.
** b) Leave in call to set tracing off when halting - until we can get tracing
**    stuff working well enough to see if call is needed.
**
**    Rev 1.28   31 Jul 1992 09:39:34   brucea
**
**
**    Rev 1.27   30 Jul 1992 18:10:44   brucea
** Added: message output to CLI if active; removed BxMessageBox
** Removed: event and code for removing matching temp bkpt
** Fixed: EnlRegister to use lpBxCallback returned from MakeProcInstance
** Removed: #ifdef DEBUG and replaced with call to ErrDisplayError
** Uncommented TrcTraceingSet(TRUE)
**
**    Rev 1.26   28 Jul 1992 21:23:36   brucea
** Removed vars no longer used; added casting to remove loss-of-significance
**    error
** Changed bkpt array access to start at 0 rather than 2
** Changed code after BkptProgramBkpt (if bkpt removed) to call BxClearBkpt
**    which dealloc's all memory for bkpt
** Bug fix: was not decrementing bkptCount when removing all internal bkpts
**
**    Rev 1.25   27 Jul 1992 23:22:08   brucea
** Added: extern HANDLE hLib, added as second param to MakeProcInstance
** Changed: BxSetSrcBkptFromCli to get the module desc from SymMapAddr2Symbol
**    instead of passed param because passed param may be function desc.
** Put TrcTracingSet call in comments until firmware modified
**
**
**    Rev 1.24   22 Jul 1992 16:45:34   doug
** conversion to generic shared data
**
**    Rev 1.23   21 Jul 1992 20:52:36   brucea
** Changed: processor-specific code is if() with proc.h call info
** Added: address check for >maxAddress and not on instruction boundary; includes
**    function BxCheckForValidAddress
** Removed: unused local vars
**
**
**    Rev 1.22   19 Jul 1992 22:33:28   brucea
** Cosmetic change of indentation
**
**    Rev 1.21   10 Jul 1992 19:42:38   brucea
** Cleaned up error reporting in BxSetSrcBkpt
**
**    Rev 1.20   23 Jun 1992 18:52:18   brucea
** Changed: Bx... to Bk... for functions moved to BkRoot
** Changed: BkProcessorMustBeHalted call to ...Absolute when called from
**    step, go and those functions which require target to be stopped first
** Removed functions: BxGetEmulationStatus, BxProcessorMustBeHalted,
**    BxProcessorMustBeHaltedAbsolute
**
**    Rev 1.19   18 Jun 1992 16:00:08   brucea
** Created: BxGetSrcBkptNames
**
**    Rev 1.18   15 Jun 1992 09:56:40   brucea
** Added: static emuRunAccess that allows system to make memory access calls to
**    emulator while it is running.
** Added functions: BxSetRunAccess, BxClearRunAccess, BxGetRunAccess,
**    BxGetBkptByAddressNext for presenter to check bkpts within address range
**    BxGetAllBkptNext for bkpt presenter to get entire list of bkpts
**    BxProcessorMustBeHaltedAbsolute which ignores emuRunAccess when testing for
**       processor running/halted
** Modified: BxProcessorMustBeHalted to ignore test for processor halted if
**    emuRunAccess is true.
**
**
**    Rev 1.17   11 May 1992 12:49:10   brucea
** Changed: name from ProcessorMustBeHalted to BxProcessorMustBeHalted
**
**    Rev 1.16   11 May 1992 12:24:02   brucea
** Reinstalled: BxTerminate
**
**    Rev 1.15   11 May 1992 09:42:42   brucea
** Modified: step routine so control stays inside step function whenever a emu
**    go is used.  The routine spins on <emuRunningSpinFlag> which is cleared
**    when the emulator halts.
** Added: function BxWaitForEmulationHalt
** Cleaned up: BxCallback, BxSrcStep
** Removed: BxTerminate since Actor code was changed to not call it
** Modified: BxGoIntoCallReturn
**
**    Rev 1.14   01 May 1992 17:31:52   brucea
** Removed: StkCalcSymbolicInfo.  This is now done internally in stk server
** Added: setting of address space to SPACE_SP before setting breakpoint so
**    hardware bkpt can be successfully programmed.
** Changed: bkpt index starts at 2 because 0 is invalid and 1 is for HW bkpt.
** Created: BxLLStartEmulation that does not mess with goState; needed for
**    goUntil and goInto operations
** Fixed: several bugs in BxLocalGoUntilCallReturn
** Fixed: U32 (was U8) declaration of localStepCount that caused parameters
**    to be off when sent to firmware for BxAsmStep
**
**    Rev 1.13   27 Apr 1992 15:01:58   mindy
** daddasmopen interface changed
**
**    Rev 1.13   27 Apr 1992 15:00:20   mindy
** daddasmopen interface changed
**
**    Rev 1.12   20 Apr 1992 09:59:52   brucea
** Added: registration on EVENT_SYMBOL_BASE_CHANGED
** Added: looping of single step for large step counts
** Fixed: BxClearBkpt so it only TFree's memory if not NULL_SYMBOL for each
**    string entry
** Bug fixes
** Cleaned up: ESC detection for BxAsmStep while in loop
**
**    Rev 1.11   08 Apr 1992 14:00:16   doug
** Check to be sure that the breakpoint is set before exiting.  It assures
** there is no race condition (the breakpoint is guaranteed to be set before
** any other command, like go, starts up).  A race condition could cause
** unpredictable results (the firmware may not be done with setting the
** breakpoint table up before a go is issued that uses the table, for
** example).
**
**    Rev 1.10   08 Apr 1992 12:49:48   brucea
** Fixed: BxAsmStep to spin on (emulState == BX_EMULATION_HALTED)
** Changed: BxStartEmulation: changed from comparing emulState not equal to
**    BX_EMULATION_HALTED to comparing equal to BX_EMULATION_RUNNING.
**
**    Rev 1.9   03 Apr 1992 18:48:22   brucea
** Added: #define SDSDUMMY to conditionally compile for hardware or no hardware
** Added: BxClearBkpt (internal), BxInsertBkptNamesAndOffset, BxSetpUntilBkpt
** Revamped: BxSetSrcBkpt, BxSetSrcBkptFromCli, BxSetBkptWithIndex to 1) save
**    name of module, reference, module name, function name, bkpt offset from
**    start of function.
** Added: ProcessorMustBeHalted check before any bkpt set
** Changed: ProcessorMustBeHalted to EXPORT
**
**
**    Rev 1.8   26 Mar 1992 18:28:34   brucea
** Added: state PRIVATE vars for module/linenum lookahead, go state and
**    granularity, temporary bkpt match
** Functions: BxGetSrcAddressRange, BxDisableSrcBkpts, BxRemoveAllInternalBkpts,
**    BxRemoveMatchingTemporary, BxLocalGoUntilCallReturn,
**    BxGoUntilCallReturnPostBkpt, BxGoIntoCallReturnPostBkpt, BxEmulStateChange,
**    BxMessageBox, BxBkptMatchInternal, BxEmulStateChange
** Added: callbacks for event notification; generating events for bkpt changes,
**    emulator started and halted.
** Other changes were made, too numerous to detail
**
**    Rev 1.7   16 Mar 1992 13:38:06   brucea
** Changed: symbols deleted event notification now causes all symbols to be
**    removed; before it only disabled them.  When disabled and displayed, their
**    symbol descriptors are invalid which caused a memory access error.
** Changed: MAX_COLUMN to SYM_MAX_COLUMN
** Fixed: increment of static bkptCount when a bkpt is added (in several places)
** Fixed: bug which set bkpt type to src when setting symbolic bkpt from CLI
**
**    Rev 1.6   12 Mar 1992 23:10:08   brucea
** Modifications: Major changes to most of the functions. Implemented source-
**    level entry of breakpoints and display of those symbolically.
** Implemented: BxCallback to remove bkpts when a load has completed, and
**    disable all src brkpts when symbols deleted
** Fixed: loops which stopped when BX_NOT_BEING_USED went true
**
**    Rev 1.5   03 Mar 1992 07:31:34   doug
** do not check to see if the processor is halted to allow it to halt
**
**    Rev 1.4   28 Feb 1992 07:41:04   doug
** bugs found during testing
**
**    Rev 1.3   17 Feb 1992 07:53:02   doug
** a) added event notification
** b) start tracing when starting emulation
** c) added checks for valid table entries
**
**    Rev 1.2   05 Feb 1992 15:42:22   doug
** a) cli is now in separate file
** b) cleaned up compile problems
**
**    Rev 1.1   03 Feb 1992 10:36:46   doug
** changed step to match firmware handshaking
**
**    Rev 1.0   01 Feb 1992 15:56:02   doug
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/bkptexec/bkptexec.c_v   1.7   03 Mar 1998 10:13:28   Eric  $
**
**  Copyright (C) 1991,92 Microtek International.  All rights reserved.
**
*****************************************************************************/


		       /****************************
			*                          *
			*       INCLUDE FILES      *
			*                          *
			****************************/
#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _BASEWIND_
#include "basetype.h"
#endif

#ifndef _BKPTEXEC_
#include "bkptexec.h"
#endif

#ifndef _BKROOT_
#include "bkroot.h"
#endif

#ifndef _BX_
#include "bx.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

#ifndef _CLIULIB_
#include "cliulib.h"
#endif

#ifndef _CPU_
#include "cpu.h"
#endif

#ifndef _DAD_SERVER_
#include "dasm.h"
#endif

#ifndef _ENLIB_
#include "enlib.h"
#endif

#ifndef _EVENTS_
#include "events.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#ifndef _SSHARED_
#include "sshared.h"
#endif

#ifndef _SDPROBE_
#include "sdprobe.h"
#endif

#ifndef _STKSERVR_
#include "stkservr.h"
#endif

#ifndef __STRING_H
#include "string.h"
#endif

#ifndef _SYMBLSVR_
#include "symblsvr.h"
#endif

#ifndef _SYMERROR_
#include "symerror.h"
#endif

#ifndef _TRACE_
#include "trace.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif

#define ICE_USER_HALT -13

RETCODE EXPORT iceGetCpuStatus(U8 *goEnd);
RETCODE EXPORT iceRunUntil(U32 addr);
RETCODE EXPORT iceStepRange(U32 addr1, U32 addr2);
RETCODE EXPORT Sds2AbiFwUnloadRegisters(BOOLEAN patchPCSP);
RETCODE EXPORT Sds2AbiFwLoadRegisters(VOID);
VOID GetReturnPC(U32 *retPC, BOOLEAN *inValid);
RETCODE EXPORT iceGetMemN(U32 addr, U8 *buff, U16 len);

		       /****************************
			*                          *
			*     GLOBAL DEFINITIONS   *
			*                          *
			****************************/

#undef DEBUG

extern HANDLE hLib;
extern HANDLE cliServerHandle;  /* return address for CLI results */

		       /****************************
			*                          *
			*     LOCAL DEFINITIONS    *
			*                          *
			****************************/
CHAR dummyNameStr[MAX_SYMNAME_LENGTH] = "";

		       /****************************
			*                          *
			*     LOCAL STORAGE        *
			*                          *
			****************************/
/*
** the current breakpoint table
*/
BX_BREAKPOINT bkptTable[MAX_NUM_BKPT];
RETCODE       globalEscPushed;   /* returns ESC error if happened
				    while in call-back */


/* bkptCount is only modified in BxRemoveBkpt and GetAvailableBkpt and
   should remain that way to keep the code robust */
PRIVATE BKPT_INDEX bkptCount;

/* holds number of internal bkpts set */
PRIVATE BKPT_INDEX bkptTempInternalCount;

/* state variables to hold current module descriptor and next linenum index */
PRIVATE SYM_DESCRIPTOR     lookaheadModule;
PRIVATE LINENUM_DESCRIPTOR lookaheadLinenumIndex;
PRIVATE DESCRIPTOR dasmID;   /* ID from opening Disassembler */

typedef enum {
   BX_GO_FOREVER,
   BX_GO_UNTIL,
   BX_GO_INTO,
   BX_GO_STEP
} BX_GO_TYPE;

typedef struct {
   SYM_DESCRIPTOR functionDesc;
   SYM_DESCRIPTOR moduleDesc;
   LPSTR funcNamePtr;
   FUNC_CLASS funcClass;
   U32 stackSize;
   DESCRIPTOR funcAddrRangeDesc;
   U8 frameType;
   U32 pushMask;
   U8 numParams;
   SYM_DESCRIPTOR firstLocal;
   U16 localListCount;
   U8 funcName[80];
} FUNC_INFO_TYPE;

/*----------------------------------------------------------
  state variable holding state of "go" and granularity type
------------------------------------------------------------*/
PRIVATE BX_GO_TYPE goState;
PRIVATE BX_GRANULARITY granState;

/*--------------------------------------------------
  used to determine when to continue host execution
----------------------------------------------------*/
PRIVATE BOOLEAN emuRunningSpinFlag;

/*----------------------------------------------------------------------
   state flag indicating stepping is taking place.  It short-circuits the
   event notification since stepping can occur in multiple bursts of
   step-stop-test_for_ESC, etc. and we only want running and halted events
   to occur once for the entire step command
------------------------------------------------------------------------*/
PRIVATE BOOLEAN steppingFlag = FALSE;

/*----------------------------------------------------------------------
   Holds information about the bkptpoint cause; part is initialized
   in BxInit only once.
   Values are assigned as follows (as defined in header of BxCliCause
   in brkexcli.c):

   0   "No cause",
   1   "Target processor was reset",
   2   "Emulator was halted",
   3   "Trigger caused a break",
   4   "Processor single step",
   5   "Execution breakpoint reached",
   6   "Overlay access violation",
   7   "Processor received a double bus fault",
   8   "Unknown break cause"
------------------------------------------------------------------------*/
PRIVATE CSERVER_VARIABLE_VALUE bkptCauseSystemVar;
PRIVATE S8 bkptCauseSystemVarName[] = "BREAKCAUSE";
PRIVATE BREAK_CAUSE lastBkptCause;


/*----------------------------------------------------------------------
   Holds information about emulation (TRUE, FALSE).  Part is initialized
   in BxInit only once.
   Values assigned are:

   0    not emulating
   1    emulating
------------------------------------------------------------------------*/
PRIVATE CSERVER_VARIABLE_VALUE emulatingSystemVar;
PRIVATE S8 emulatingSystemVarName[] = "EMULATING";

#define BX_NOT_EMULATING 0L
#define BX_EMULATING     1L


/*------------------------------------------------------------------------
   frequently-used descriptors obtained at initialization
--------------------------------------------------------------------------*/
PRIVATE DESCRIPTOR emulStateDesc;
PRIVATE DESCRIPTOR emulResultDesc;
PRIVATE DESCRIPTOR singleStepDesc;
PRIVATE DESCRIPTOR goDesc;
PRIVATE DESCRIPTOR resetAndGoDesc;
PRIVATE DESCRIPTOR haltDesc;
PRIVATE DESCRIPTOR bkAddrDesc, bkSpaceDesc, bkIdDesc, bkCmdDesc,
		   bkEnableDesc, bkResultDesc;

/*------------------------------------------------------------------------
   processor-dependent flags and data
--------------------------------------------------------------------------*/
PROCESSOR_FAMILY staticProcFamily;
PROC_SYSTEM_TYPE staticSystemType;


/*------------------------------------------------------------------------
   flag to indicate if a demonstration (no hardware) version is being used
--------------------------------------------------------------------------*/
PRIVATE BOOLEAN demonstrationVersion = FALSE;

		       /****************************
			*                          *
			*        PROTOTYPES        *
			*                          *
			****************************/

/*---------------------------------------------------------------------------
** BxGetSrcAddressRange
**
** Purpose: Get the address range of the line/stmt that matches the current
**          PC.  Can use caching of linenum index to speed up process.
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGetSrcAddressRange(DESCRIPTOR     currentPC,
		     BX_GRANULARITY granularity,
		     DESCRIPTOR FAR *rangeDesc);

/*---------------------------------------------------------------------------
** BxRemoveAllInternalBkpts
**
** Purpose: delete all temporary internal bkpts
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxRemoveAllInternalBkpts(VOID);

/*---------------------------------------------------------------------------
** BxRemoveMatchingTemporary
**
** Purpose: if the currentPC matches a temporary bkpt, remove it
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxRemoveMatchingTemporary(BOOLEAN *tempMatch);

/*---------------------------------------------------------------------------
** BxLocalGoUntilCallReturn
**
** Purpose:
**    Implement algorithm except for setting state and granularity status
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxLocalGoUntilCallReturn(BX_GRANULARITY      gran,
			 BX_CALL_RETURN_TYPE callReturnType);

/*---------------------------------------------------------------------------
** BxGoUntilCallReturnPostBkpt
**
** Purpose: removes all temporary internal bkpts when called by emulation
**          halted callback
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGoUntilCallReturnPostBkpt(VOID);

/*---------------------------------------------------------------------------
** BxGoIntoCallReturnPostBkpt
**
** Purpose: removes all temporary internal bkpts when called by emulation
**          halted callback.  If not on the start of a line, steps thru the
**          line.
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGoIntoCallReturnPostBkpt(BOOLEAN bkptWasSet);

/*---------------------------------------------------------------------------
** BxEmulStateChange
----------------------------------------------------------------------------*/
VOID EXPORT BxEmulStateChange(DESCRIPTOR desc);

/*---------------------------------------------------------------------------
** BxStepUntilBkpt
**
** Purpose: internal debug routine to simulate execution until a breakpoint.
**          Disassembles instruction, checks for address in internal bkpt
**          list.
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxStepUntilBkpt(VOID);

/*---------------------------------------------------------------------------
** BxClearBkpt
----------------------------------------------------------------------------*/
RETCODE BxClearBkpt(BKPT_INDEX index);

/*---------------------------------------------------------------------------
** BxRemoveBkptAllocation
**
** Frees up any memory allocated for a bkpt; includes address, names, and
**    strings
----------------------------------------------------------------------------*/
RETCODE BxRemoveBkptAllocation(BKPT_INDEX index);

/*---------------------------------------------------------------------------
** BxInsertBkptNamesAndOffset
----------------------------------------------------------------------------*/
RETCODE BxInsertBkptNamesAndOffset(BKPT_INDEX index);

/*---------------------------------------------------------------------------
** BxMLStartEmulation - medium level; not cli or source-level call
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxMLStartEmulation(BX_GO_TYPE inputState);

/*---------------------------------------------------------------------------
** BxLLStartEmulation
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxLLStartEmulation(VOID);

/*---------------------------------------------------------------------------
** BxWaitForEmulationHalt
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxWaitForEmulationHalt(VOID);

/*----------------------------------------------------------------------
**  BxLLHaltEmulation
------------------------------------------------------------------------*/
RETCODE BxLLHaltEmulation(VOID);

/*----------------------------------------------------------------------
**  BxLLAsmStepInto
------------------------------------------------------------------------*/
RETCODE BxLLAsmStepInto(U32 stepCount);

/*----------------------------------------------------------------------
** BxLLAsmStepOver
**
** Purpose: implements step over mode; uses bkpt when JSR encountered
------------------------------------------------------------------------*/
RETCODE BxLLAsmStepOver(U32 stepCount);

/*----------------------------------------------------------------------
** BxHeapDump
**
** Purpose: dump heap memory info
------------------------------------------------------------------------*/
RETCODE BxHeapDump(VOID);


RETCODE PRIVATE BxSrcStepFast(BX_STEP_TYPE type, BX_GRANULARITY gran,
      DESCRIPTOR *pcAddrDesc, DESCRIPTOR *srcRangeDesc);
RETCODE PRIVATE BxSrcStepSlow(BX_STEP_TYPE type, BX_GRANULARITY gran,
      DESCRIPTOR *pcAddrDesc, DESCRIPTOR *srcRangeDesc);
RETCODE PRIVATE BxIsFunction(DESCRIPTOR addrDesc, BOOLEAN *isFunc);

RETCODE InitStepMask(VOID);

			/****************************
			 *                          *
			 *      EXECUTABLE CODE     *
			 *                          *
			 ****************************/

/*----------------------------------------------------------------------
** BxHeapDump
**
** Purpose: dump heap memory info
------------------------------------------------------------------------*/
RETCODE BxHeapDump(VOID) {

   U32 amountAllocated, amountFreed;
   S8  buff[80];

   HeapDump(&amountAllocated, &amountFreed);
   sprintf((LPSTR)buff, (LPSTR)"Allocated: %ld, Freed: %ld, Active: %ld",
	   amountAllocated, amountFreed, amountAllocated-amountFreed);
   BxSendCliMessage((LPSTR)buff);
   return GOOD;
}  /* end of BxHeapDump */


/*---------------------------------------------------------------------------
**  BxInit
----------------------------------------------------------------------------*/
RETCODE EXPORT BxInit(VOID) {

   RETCODE err, err1;
   BKPT_INDEX i;
   DESCRIPTOR dasmAddrDesc;
   FARPROC lpEmulProc, lpBxCallback;
   DESCRIPTOR descEvt;  /* unused return param from EnlRegister */

   if((err = ProcReturnDemonstrationVersion(&demonstrationVersion))!=GOOD)
      return(err);
   if((err = InitStepMask()) != GOOD) return(err);
   lpEmulProc = MakeProcInstance((FARPROC) BxEmulStateChange, hLib);
   if((err = SdRegister(SD_EMULATION_STATE,
			lpEmulProc,
			&emulStateDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_EMULATION_RESULT,
			NULL,
			&emulResultDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_STEP, NULLPTR, &singleStepDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_GO, NULLPTR, &goDesc))!=GOOD) return err;
   if((err = SdRegister(SD_GR, NULLPTR, &resetAndGoDesc))!=GOOD) return err;
   if((err = SdRegister(SD_HALT, NULLPTR, &haltDesc))!=GOOD) return err;
   if((err = SdRegister(SD_BKPT_ADDR, NULLPTR, &bkAddrDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_BKPT_SPACE, NULLPTR, &bkSpaceDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_BKPT_ID, NULLPTR, &bkIdDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_BKPT_CMD, NULLPTR, &bkCmdDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_BKPT_ENABLE, NULLPTR, &bkEnableDesc))!=GOOD)
      return err;
   if((err = SdRegister(SD_BKPT_RESULT, NULLPTR, &bkResultDesc))!=GOOD)
      return err;

   {
      BKPT_CMD bkptCmd = BKPT_CLEAR_ALL;
      BOOLEAN timedOut;
      if((err = SdWriteCmdReadResponse(bkCmdDesc, (U8 FAR *)&bkptCmd, GOOD,
	 bkResultDesc, 0L, &timedOut)) != GOOD) return(err);
   }

   /* register to receive events from ... */
   lpBxCallback = MakeProcInstance((FARPROC) BxCallback, hLib);
   if ((err = EnlRegister(EVENT_SYMBOL_INIT_LOAD,
			  (EVCALLBACK) lpBxCallback,
			  &descEvt))!=GOOD)
      return err;
   if ((err = EnlRegister(EVENT_SYMBOL_DELETED,
			  (EVCALLBACK) lpBxCallback,
			  &descEvt))!=GOOD)
      return err;
   /* base addr modified */
   if ((err = EnlRegister(EVENT_SYMBOL_BASE_CHANGED,
			  (EVCALLBACK) lpBxCallback,
			  &descEvt))!=GOOD)
      return err;
   if ((err = EnlRegister(EVENT_BKPT_HALTED,
			  (EVCALLBACK) lpBxCallback,
			  &descEvt))!=GOOD)
      return err;

   /*               Modified by Dennis Lamb, June 10, 1994.           */
   /* Added this to handle the case where a program is loaded without */
   /* any symbols. This fixes a bug where the breakpoints were not    */
   /* removed when the program was loaded without symbols.            */
   if ((err = EnlRegister(EVENT_LDR_LOADCOMPLETE,
			  (EVCALLBACK) lpBxCallback,
			  &descEvt))!=GOOD)
      return err;


   /* init the bkpt array */
   for (i = 0; i < MAX_NUM_BKPT; i++)
      bkptTable[i].type = BX_NOT_BEING_USED;

   /* initialize statics */
   bkptCount = 0;  /* no bkpts */
   bkptTempInternalCount = 0;
   lookaheadModule = NULL_SYMBOL;
   lookaheadLinenumIndex = 0;
   emuRunningSpinFlag = FALSE;

   /* get processor type to remove processor dependencies */
   if (GOOD != (err = ProcReturnProcFamily(&staticProcFamily)))
      return err;
   if((err = ProcReturnSystemType(&staticSystemType))!=GOOD) return err;
   
   /* use the current PC instead of the default address descriptor */
   if (GOOD != (err = CpuGetPC(&dasmAddrDesc)))
      return err;
   /* initialize dasmID to be used for life of bkpt server */
   err = DadDasmOpen(&dasmID, dasmAddrDesc);
   err1 = AdrDestroyAddress(dasmAddrDesc);
   if (err)
      return err;
   if (err1)
      return err1;

   /* set up system variables; must malloc space for system var names */
   bkptCauseSystemVar.integerValueFlag = 1;  /* specify integer system var */
   emulatingSystemVar.integerValueFlag = 1;

   bkptCauseSystemVar.integerValue = 0L;   /* no cause */
   emulatingSystemVar.integerValue = 0L;   /* not emulating */

   bkptCauseSystemVar.variableName = &bkptCauseSystemVarName[0];
   emulatingSystemVar.variableName = &emulatingSystemVarName[0];

   /* not necessary to initialize the stringValue field because this data
      structure will never by deallocated (since it is static, it cannot
      be).  This means that the call must always specify no freeing of mem */

   lastBkptCause = 0;
   return GOOD;
}  /* end of BxInit */


/*---------------------------------------------------------------------------
**  BxCallback
**
**  Purpose:
**     Is called when event that the bkpt server has registered on occurs
----------------------------------------------------------------------------*/
RETCODE EXPORT BxCallback(U32 eventNum) {

   RETCODE err, firstErr;
   HANDLE hCLIModule;
   LPSTR      buf;
   U16        bufLength;
   DESCRIPTOR currentPCDesc;

   switch (eventNum)  {
      /* !!! needs to be the event which occurs when the load is complete */
      /* eventually want to recalculate symbolic bkpts when new load done;
	 this requires saving module/func name, func size, plus offset from
	 start of function in order to recalculate bkpt from function
	 beginning.  The criteria for recalculating symbolic bkpts is if the
	 function size hasn't changed */
	  /* Added EVENT_LDR_LOADCOMPLETE to handle the situation when a program
	     is loaded without symbols. */
	  case EVENT_LDR_LOADCOMPLETE:
      case EVENT_SYMBOL_INIT_LOAD:
	 err = BxRemoveAllBkpts();
	    if (err) {
	       ErrDisplayError(err, CHECK_MODE);
	    }
	    break;

      case EVENT_SYMBOL_DELETED:
      case EVENT_SYMBOL_BASE_CHANGED:
	 /* !!! for now, remove all bkpts; eventually must tag bkpt with flag
	    indicating no symbols; then recalc when symbols reloaded */
	 err = BxRemoveAllBkpts();
	    if (err) {
	       ErrDisplayError(err, CHECK_MODE);
	    }
	 break;

      case EVENT_BKPT_HALTED: /* HL emulation halted */
	 /* display address and next instruction if in CLI */
	 if (GOOD != (firstErr = CLIGetActive(&hCLIModule))) {
	    goto CLEANUP_1;
	 }
	 if (!hCLIModule) {
	    break;  /* not in CLI */
	 } else {
	    if (GOOD !=
	       (firstErr = CpuGetPC(&currentPCDesc))) { /* alloc's addr */
	       goto CLEANUP_1;
	    }
	    if (GOOD != (firstErr = DadSetDasmAddr(dasmID, currentPCDesc))) {
	       goto CLEANUP_2;
	    }
	    /* disassemble next instruction */
	    /* buf storage is alloc'ed by this call; must be TFree'd
	       before leaving function */
	    if (GOOD !=
	       (firstErr = DadDasmInst(dasmID, 1, &buf, &bufLength))) {
	       goto CLEANUP_2;
	    }
	    /* send out the text */
	    firstErr = BxSendCliMessage(buf);
	 }
	 err = TFree((LPSTR)(buf));

      CLEANUP_2:
	 err = AdrDestroyAddress(currentPCDesc);
	 if (GOOD == firstErr)
	    firstErr = err;

      CLEANUP_1:
	 if (GOOD != firstErr) {
	    if (ER_ABORT_FROM_ESC == firstErr) {
	       globalEscPushed = ER_ABORT_FROM_ESC;
	    } else {
	       ErrDisplayError(firstErr, CHECK_MODE);
	    }
	 }
	 break;

      default:
	 ErrDisplayError(ER_BX_INVALID_CALLBACK, CHECK_MODE);

   } /* end of switch */
   return GOOD;  /* callback caller ignores error value */
}  /* end of BxCallback */


/*---------------------------------------------------------------------------
**  BxTerminate
----------------------------------------------------------------------------*/
RETCODE EXPORT BxTerminate(VOID) {

   RETCODE err, firstErr;

   firstErr = SdUnRegister(emulStateDesc);
   err = SdUnRegister(singleStepDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(emulResultDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(goDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(resetAndGoDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(haltDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkAddrDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkSpaceDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkIdDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkCmdDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkEnableDesc);
   if (GOOD == firstErr) firstErr = err;
   err = SdUnRegister(bkResultDesc);
   if (GOOD == firstErr) firstErr = err;
   err = DadDasmClose(dasmID);
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
}


/*---------------------------------------------------------------------------
**  BxSetSrcBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT
BxSetSrcBkpt(BX_BPSTATE     state,
	     BX_BPLIFE      life,
	     SYM_DESCRIPTOR module,
	     LINENUM_TYPE   lineNum,
	     COLUMN_TYPE    columnStart,
	     LPSTR          lineText,
	     DESCRIPTOR     bkptRangeDesc,
	     DESCRIPTOR FAR *desc) {

   RETCODE            err;
   LINENUM_TYPE       actualLineNum;
   COLUMN_RANGE_TYPE  columnRange;
   LINENUM_DESCRIPTOR nextIndex;
   BKPT_INDEX         index;
   SYM_DESCRIPTOR     lowestLevelDesc, functionDesc, moduleDesc;
   MEM_ADDR_CLASS     memoryClass;
   SYM_TYPE_TYPE      symbolType;
   U32                symbolOffset;
   U16                bufLen;
   BOOLEAN            dummy;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   /* remove processor dependency */
   if (FAMILY_68K == staticProcFamily) {
      if((err = AdrSetAddrSpace(bkptRangeDesc, SPACE_SP)) != GOOD) {
	 AdrDestroyAddress(bkptRangeDesc);
	 return err;
      }
   }
   /* Get Bkpt address range */
   if (columnStart)  {
      if((err = SymGetLinenumStmt(module, lineNum, columnStart, bkptRangeDesc,
				  &actualLineNum, &columnRange, &nextIndex))
	 !=GOOD) {
	 AdrDestroyAddress(bkptRangeDesc);
	 return err;
      }
      /* Winky 12/3/97 */
      //if (lineNum != actualLineNum) { //remove by Eric 12/31/97
	 columnRange.columnStart = 0;
	 columnRange.columnEnd = SYM_MAX_COLUMN;
      //}
      /* EOF Winky 12/3/97 */
   } else {
      if((err = SymGetLinenum(module, lineNum, bkptRangeDesc, &actualLineNum,
			      &(columnRange.columnStart), &nextIndex))
	 !=GOOD) {
	 AdrDestroyAddress(bkptRangeDesc);
	 return err;
      }
      /* fill in end range, since SymGetLinenum doesn't */
      columnRange.columnEnd = SYM_MAX_COLUMN;
   }

   /* Convert bkptRangeDesc to ADDR_LINEAR type */
   if ((err = AdrConvertAddress(bkptRangeDesc, ADDR_LINEAR, 
		(BOOLEAN FAR *)&dummy)) != GOOD) {
      AdrDestroyAddress(bkptRangeDesc);
      return err;
   }

   /* Check for Location already has breakpoint and bkpt availability */
   if ((err = AddrHasNoBkpt(bkptRangeDesc, life)) != GOOD) {
      AdrDestroyAddress(bkptRangeDesc);
      // Exit, report warning handled by AddrHasNoBkpt()
      return(err == ER_BX_ALREADY_HAS_BKPT ? GOOD : err);
   }
   if ((err = GetAvailableBkpt(&index)) != GOOD) {
      AdrDestroyAddress(bkptRangeDesc);
      return err;
   }
   /* free memory and init before putting new values in */
   if (GOOD != (err = BxClearBkpt(index))) {
      AdrDestroyAddress(bkptRangeDesc);
      return err;
   }
   /*--------------------------*/
   /* fill in bkpt information */
   /*--------------------------*/
   bkptTable[index].state = state;
   bkptTable[index].life = life;
   bkptTable[index].symMatch = TRUE;
   bkptTable[index].module = module;
   bkptTable[index].lineNumber = actualLineNum;
   bkptTable[index].columnRange = columnRange;
   bkptTable[index].addr = bkptRangeDesc;

   /*---------------------*/
   /* fill in name fields */
   /*---------------------*/
   /* get symbolic information including function and module */
   /* look up module, function by address                    */
   err = SymMapAddr2Symbol(bkptRangeDesc,
			   &memoryClass,
			   &symbolType,
			   &symbolOffset,
			   &lowestLevelDesc,
			   &functionDesc,
			   &moduleDesc);
   if (err != GOOD) {
      /* fall out if the symbol lookup is not found; means that no
	 symbolics attached to bkpt */
      if (ER_ADDRESS_NOT_FOUND != err) { /* error but not NOT_FOUND err */
	 return err;
      }
   } else { /* good symbol lookup */
      bkptTable[index].function = functionDesc;  /* could be NULL_SYMBOL */
      if (moduleDesc != NULL_SYMBOL) {
	 if (GOOD != (err = BxInsertBkptNamesAndOffset(index)))
	    return err;
      }
   } /* end of symbol stuffing */

   /*--------------------*/
   /* put in source line */
   /*--------------------*/
   bufLen = strlen(lineText);
   bkptTable[index].statementTextPtr = TMalloc((U32)(bufLen+1));
   if (!bkptTable[index].statementTextPtr) {
      return ER_OUT_OF_MEMORY;
   }
   if (bufLen == 0) {
      bkptTable[index].statementTextPtr[0] = '\0';
   } else {
      strcpy(bkptTable[index].statementTextPtr, &lineText[0]);
   }
   {
   U16 num;
   /* must get unused .cliBkptNumber and save, since all bkpts get one */
   if (GOOD != (err = CreateCliBkptNum(&num)))
      return err;
   bkptTable[index].cliBkptNumber = num;
   }

   /* NOTE: up until this point, if an error occurs, bkpt has not been
      set as "used" */
   if (BX_TEMPORARY_INTERNAL == life) {
      bkptTable[index].type = BX_SET_INTERNAL;
      bkptTempInternalCount++;
   } else {
      bkptTable[index].type = BX_SET_FROM_SRC;
   }
   *desc = (DESCRIPTOR)index;
   bkptCount++;

   if (bkptTable[index].state == BX_ENABLED) {  /* only program if enabled */
      if (GOOD != (err = BkptProgramBkpt(index))) {
	 /* if failed, clear breakpoint */
	 BxRemoveBkpt(*desc);
	 return err;
      }
   }
   if (BX_TEMPORARY_INTERNAL != life) {
      return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
}  /* end of BxSetSrcBkpt */


/*---------------------------------------------------------------------------
** BxSetSrcBkptFromCli
**
** Purpose: sets a source-level bkpt from the CLI input
**
** Input parameters:
**    index: which bkpt in array to change
**    state:
**    life:
**    module: symbol descriptor; may not be a module
**    actualLinenum, actualColumnStart, actualColumnEnd: validated before
**       entering this function
**    lineText: presently test string; will become NULL
**    bkptAddr: address desc of bkpt; stored in bkpt structure; i.e. consumed
**    desc: return opaque value for accessing same bkpt
----------------------------------------------------------------------------*/
#pragma argsused
RETCODE
BxSetSrcBkptFromCli(BKPT_INDEX     index,
		    BX_BPSTATE     state,
		    BX_BPLIFE      life,
		    SYM_DESCRIPTOR symbol,   /* may not be a module */
		    LINENUM_TYPE   actualLinenum,
		    COLUMN_TYPE    actualColumnStart,
		    COLUMN_TYPE    actualColumnEnd,
		    LPSTR          lineText,
		    DESCRIPTOR     bkptAddr,
		    U16            cliBkptNum,
		    DESCRIPTOR FAR *desc)  {

   RETCODE        err;
   U16            bufLen;
   SYM_DESCRIPTOR lowestLevelDesc, functionDesc, moduleDesc;
   MEM_ADDR_CLASS memoryClass;
   SYM_TYPE_TYPE  symbolType;
   U32            symbolOffset;
   BOOLEAN        isValid, dummy;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
       
   /* Convert bkptRangeDesc to ADDR_LINEAR type */
   if ((err = AdrConvertAddress(bkptAddr, ADDR_LINEAR, 
		(BOOLEAN FAR *)&dummy)) != GOOD)  return err;

   if (GOOD !=
      (err = AdrIsValidCodeAddress(bkptAddr, (BOOLEAN FAR *)&isValid)))
      return err;
   if (!isValid) {
      return ER_BX_NOT_ON_INST_BOUNDARY;
   }
   /* free memory and init before putting new values in */
   if (GOOD != (err = BxClearBkpt(index)))
      return err;
   /*--------------------------*/
   /* fill in bkpt information */
   /*--------------------------*/
   bkptTable[index].state = state;
   bkptTable[index].life = life;
   bkptTable[index].symMatch = TRUE;
   bkptTable[index].addr = bkptAddr;
   bkptTable[index].cliBkptNumber = cliBkptNum;
   bkptTable[index].lineNumber = actualLinenum;
   bkptTable[index].columnRange.columnStart = actualColumnStart;
   bkptTable[index].columnRange.columnEnd = actualColumnEnd;
   bkptTable[index].module = NULL_SYMBOL;

   /*---------------------*/
   /* fill in name fields */
   /*---------------------*/
   /* get symbolic information including function and module */
   /* look up module, function by address */
   err = SymMapAddr2Symbol(bkptAddr,
			   &memoryClass,
			   &symbolType,
			   &symbolOffset,
			   &lowestLevelDesc,
			   &functionDesc,
			   &moduleDesc);
   
   if (GOOD != err) {
      // fall out if the symbol lookup is not found; means that no
      // symbolics attached to bkpt
      // error but not NOT_FOUND err. This is OK for Assembly module
      if (ER_ADDRESS_NOT_FOUND != err) { 
	 return err;
      }
   } else { /* good symbol lookup */
      if (memoryClass == CODE_ADDR) {
	 bkptTable[index].module = moduleDesc;
	 bkptTable[index].function = functionDesc;  /* could be NULL_SYMBOL */
	 if (moduleDesc != NULL_SYMBOL) {
	    if (GOOD != (err = BxInsertBkptNamesAndOffset(index)))
	       return err;
	 }
      }
   } /* end of symbol stuffing */

   /* since this function modifies existing bkpts as well as storing
      into new ones, only want to count if it is a new one */
   if (bkptTable[index].type == BX_NOT_BEING_USED) {
      bkptCount++;
   }
   /*--------------------*/
   /* put in source line */
   /*--------------------*/
   bufLen = strlen(lineText);
   bkptTable[index].statementTextPtr = TMalloc((U32)(bufLen+1));
   if (!bkptTable[index].statementTextPtr) {
      return ER_OUT_OF_MEMORY;
   }
   if (0 == bufLen) {
      bkptTable[index].statementTextPtr[0] = '\0';
   } else {
      strcpy(bkptTable[index].statementTextPtr, &lineText[0]);
   }
   /* set type, send to emulator, pass back index */
   bkptTable[index].type = BX_SET_FROM_CLI;
   *desc = (DESCRIPTOR)index;
   if (bkptTable[index].state == BX_ENABLED) {  /* only program if enabled */
      if (GOOD != (err = BkptProgramBkpt(index))) {
	 /* if failed, clear breakpoint */
	 BxRemoveBkpt(*desc);
	 return err;
      }
   }
   return EnlEventNotify(EVENT_BKPT_EDIT);

} /* end of BxSetSrcBkptFromCli */


/*---------------------------------------------------------------------------
**  BxGetSrcBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT BxGetSrcBkpt(DESCRIPTOR         desc,
			    BX_BPSTATE FAR     *state,
			    BX_BPLIFE FAR      *life,
			    SYM_DESCRIPTOR FAR *module,
			    LINENUM_TYPE FAR   *linenum,
			    COLUMN_TYPE FAR    *columnStart,
			    COLUMN_TYPE FAR    *columnEnd) {

   if ((BKPT_INDEX)desc >= MAX_NUM_BKPT)
      return ER_BX_INVALID_DESCRIPTOR;
   if(bkptTable[(BKPT_INDEX)desc].type == BX_NOT_BEING_USED)
      return ER_BX_INACTIVE_BKPT;

   *state = bkptTable[(BKPT_INDEX)desc].state;
   *life = bkptTable[(BKPT_INDEX)desc].life;
   *module = bkptTable[(BKPT_INDEX)desc].module;
   *linenum = bkptTable[(BKPT_INDEX)desc].lineNumber;
   *columnStart = bkptTable[(BKPT_INDEX)desc].columnRange.columnStart;
   *columnEnd = bkptTable[(BKPT_INDEX)desc].columnRange.columnEnd;
   return GOOD;
}  /* end of BxGetSrcBkpt */


/*---------------------------------------------------------------------------
**  BxGetSrcBkptNames
----------------------------------------------------------------------------*/
RETCODE EXPORT BxGetSrcBkptNames(DESCRIPTOR     desc,
				 DESCRIPTOR FAR *modRefName,
				 DESCRIPTOR FAR *modName,
				 DESCRIPTOR FAR *functionName) {

   if ((BKPT_INDEX)desc >= MAX_NUM_BKPT)
      return ER_BX_INVALID_DESCRIPTOR;
   if (bkptTable[(BKPT_INDEX)desc].type==BX_NOT_BEING_USED)
      return ER_BX_INACTIVE_BKPT;

   *modRefName   = bkptTable[(BKPT_INDEX)desc].modRefName;
   *modName      = bkptTable[(BKPT_INDEX)desc].modName;
   *functionName = bkptTable[(BKPT_INDEX)desc].functionName;
   return GOOD;
}  /* end of BxGetSrcBkptNames */


/*---------------------------------------------------------------------------
** BxGetSrcBkptNext
**
** Design:
**    check for valid moduleFilter
**    check for *loopDesc with bounds of array index
**    search from *loopDesc to find next moduleFilter descriptor
**       if found,
**          return bkptDesc
**          return present loopDesc + 1
**       if not found OR at end of loop, return ER_BX_LOOP_COMPLETE
----------------------------------------------------------------------------*/
#pragma warn -rvl
RETCODE EXPORT
BxGetSrcBkptNext(SYM_DESCRIPTOR moduleFilter,
		 DESCRIPTOR FAR *loopDesc,
		 DESCRIPTOR FAR *bkptDesc)  {

   if (moduleFilter == NULL_SYMBOL)
      return ER_BX_LOOP_COMPLETE;

   while (TRUE)  {
      if (*loopDesc >= MAX_NUM_BKPT) /* loopDesc is 0-based index */
	 return ER_BX_LOOP_COMPLETE;

      if ((bkptTable[(BKPT_INDEX)(*loopDesc)].module == moduleFilter) &&
	  (bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_NOT_BEING_USED) &&
	  (bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_SET_INTERNAL)) {
	 *bkptDesc = *loopDesc;
	 (*loopDesc)++;
	 return GOOD;
      }
      (*loopDesc)++;
   } /* end while */
}  /* end of BxGetSrcBkptNext */


/*---------------------------------------------------------------------------
** BxGetBkptByAddressNext
**
** Design:
**    check for *loopDesc with bounds of array index
**    search from *loopDesc to find match of bkpt with addrDesc range
**       if found,
**          return bkptDesc
**          return present loopDesc + 1
**       if no match OR at end of loop, return ER_BX_LOOP_COMPLETE
----------------------------------------------------------------------------*/
#pragma warn -rvl
RETCODE EXPORT
BxGetBkptByAddressNext(DESCRIPTOR addrDesc,
		       DESCRIPTOR FAR *loopDesc,
		       DESCRIPTOR FAR *bkptDesc) {
   RETCODE err;
   BOOLEAN isInRange = FALSE;

   while (TRUE)  {
      if (*loopDesc >= MAX_NUM_BKPT) /* loopDesc is 0-based index */
	 return ER_BX_LOOP_COMPLETE;

      if ((bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_NOT_BEING_USED) &&
	  (bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_SET_INTERNAL)) {
	 err = AdrIsPhysicalAddrInRange(
				bkptTable[(BKPT_INDEX)(*loopDesc)].addr,
				addrDesc,
				&isInRange);
	 if (GOOD != err) {
	    /* make sure caller won't continue to loop in case they don't
	       check for error condition */
	    *loopDesc = MAX_NUM_BKPT;
	    return err;
	 }
         if (isInRange) {
	    *bkptDesc = *loopDesc;
	    (*loopDesc)++;
	    return GOOD;
	 }
     }
     (*loopDesc)++;
   } /* end while */
}  /* end of BxGetBkptByAddressNext */


/*---------------------------------------------------------------------------
** BxGetSrcBkptNext
**
** Design:
**    check for *loopDesc with bounds of array index
**    search from *loopDesc to find next valid bkpt descriptor
**       if found,
**          return bkptDesc
**          return present loopDesc + 1
**       if not found OR at end of loop, return ER_BX_LOOP_COMPLETE
----------------------------------------------------------------------------*/
#pragma warn -rvl
RETCODE EXPORT
BxGetAllBkptNext(DESCRIPTOR FAR *loopDesc,
		 DESCRIPTOR FAR *bkptDesc)  {

   while (TRUE)  {
      if (*loopDesc >= MAX_NUM_BKPT) /* loopDesc is 0-based index */
	 return ER_BX_LOOP_COMPLETE;

      if ((bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_NOT_BEING_USED) &&
	  (bkptTable[(BKPT_INDEX)(*loopDesc)].type != BX_SET_INTERNAL)) {
	 *bkptDesc = *loopDesc;
	 (*loopDesc)++;
	 return GOOD;
      }
      (*loopDesc)++;
   } /* end while */
}  /* end of BxGetAllBkptNext */


/*---------------------------------------------------------------------------
** BxSetBkpt - asm/address only setup (no symbolic input)
----------------------------------------------------------------------------*/
RETCODE EXPORT BxSetBkpt(BX_BPSTATE state, BX_BPLIFE life,
			 DESCRIPTOR addr, DESCRIPTOR FAR *desc) {
   RETCODE    err;
   BKPT_INDEX index;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      goto CLEANUP;
   /* remove processor dependency */
   if (FAMILY_68K == staticProcFamily) {
      if((err = AdrSetAddrSpace(addr, SPACE_SP)) != GOOD) {
	 AdrDestroyAddress(addr);
	 return err;
      }
   }

   /* Check for bkpt duplication */
   if ((err = AddrHasNoBkpt(addr, life)) != GOOD) {
      AdrDestroyAddress(addr);   /* remove desc before returning */
      // Exit, report warning handled by AddrHasNoBkpt()
      return(err == ER_BX_ALREADY_HAS_BKPT ? GOOD : err);
   }
   
   if ((err = GetAvailableBkpt(&index))!=GOOD)
      goto CLEANUP;
   return BxSetBkptWithIndex(index, state, life, addr, NOT_CLI, desc);

CLEANUP:
   AdrDestroyAddress(addr);   /* remove memory before returning error */
   return err;
   } /* end of BxSetBkpt */


/*---------------------------------------------------------------------------
** BxSetBkptWithIndex - asm/address only setup which requires index input
**
** Parameters:
**    input:
**       index: bkpt index pointing into array
**       state: initial state of the breakpoint
**       life:  temporary or permanent lifetime
**       bkptAddr: address of breakpoint; consumed by this function even
**                 if it returns an error. This function will convert
**                 the address to LINEAR address type.
**       cliBkptNum: which user-entered bkpt number; NOT_CLI is used to
**          represent setting bkpt from caller other than CLI.
**
**    output:
**       desc:
**          descriptor representing the breakpoint (used to get
**          information and can be used to remove the breakpoint)
----------------------------------------------------------------------------*/
RETCODE
BxSetBkptWithIndex(BKPT_INDEX     index,
		   BX_BPSTATE     state,
		   BX_BPLIFE      life,
		   DESCRIPTOR     bkptAddr,
		   U16            cliBkptNum,
		   DESCRIPTOR FAR *desc) {

   RETCODE            err, firstErr = GOOD;
   DESCRIPTOR         addrRangeDesc;
   SYM_DESCRIPTOR     lowestLevelDesc, functionDesc, moduleDesc;
   LINENUM_TYPE       linenum;
   COLUMN_RANGE_TYPE  columnRange;
   LINENUM_DESCRIPTOR nextIndex;
   MEM_ADDR_CLASS     memoryClass;
   SYM_TYPE_TYPE      symbolType;
   U32                symbolOffset;
   BOOLEAN            isValid, dummy;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      goto CLEANUP_2;
       
   /* Convert bkptAddr to ADDR_LINEAR type */
   if ((err = AdrConvertAddress(bkptAddr, ADDR_LINEAR, 
		(BOOLEAN FAR *)&dummy)) != GOOD) goto CLEANUP_2; 
   
   if(GOOD !=
      (err = AdrIsValidCodeAddress(bkptAddr, (BOOLEAN FAR *)&isValid)))
      goto CLEANUP_2;
   if (!isValid) {
      err = ER_BX_NOT_ON_INST_BOUNDARY;
      goto CLEANUP_2;
   }
   /* free memory and init before putting new values in */
   if (GOOD != (err = BxClearBkpt(index))) {
      goto CLEANUP_2;
   }
   /* enter default values (assumes no symbol match) */
   bkptTable[index].symMatch = FALSE;
   bkptTable[index].module = NULL_SYMBOL;
   bkptTable[index].function = NULL_SYMBOL;
   bkptTable[index].addr = bkptAddr;
   bkptTable[index].state = state;
   bkptTable[index].life = life;

   /* If the breakpoint is internal - skip over symbolic filling stuffs
   ** since the life of the internal breakpoint would be very short.
   */
   if (life == BX_TEMPORARY_INTERNAL)
      goto INTERNAL_BKPT;
   
   /*
   ** get symbolic information including function and module
   */
   /* duplicate address to preserve address information */
   if((err = AdrDuplicateAddress(bkptAddr, &addrRangeDesc))!=GOOD)
      goto CLEANUP_2;
   
   /* remove processor dependency */
   if (FAMILY_68K == staticProcFamily) {
      if(GOOD != (err = AdrSetAddrSpace(addrRangeDesc, SPACE_SP)))
	 goto CLEANUP_1;
   }
   /* look up module, function by address */
   err = SymMapAddr2Symbol(bkptAddr,
			   &memoryClass,
			   &symbolType,
			   &symbolOffset,
			   &lowestLevelDesc,
			   &functionDesc,
			   &moduleDesc);
   
   if (err != GOOD) {
      if (ER_ADDRESS_NOT_FOUND != err) { /* error but not NOT_FOUND err */
	 goto CLEANUP_1;
      }
   } else { /* good symbol lookup */
      // Make sure that symbol look up is mapped to a code address
      if (memoryClass != CODE_ADDR) {
	 goto NOSYMBOL_BKPT;
      }
      if (functionDesc == NULL)
	 functionDesc = moduleDesc; //Hera 5/12/97
      err = SymMapAddr2LinenumStmt(bkptAddr,
				   moduleDesc,
				   functionDesc,
				   &linenum,
				   &columnRange,
				   addrRangeDesc,
				   &nextIndex);
      if (GOOD != err) {
	 /* error but not NOT_FOUND err and NO_LINENUM_ADDED err */
	 /* NOTES: 09/21/93 - Nghia
	 ** Support ASM module which does not have line number information
	 */
	 if ((ER_ADDRESS_NOT_FOUND != err) &&
	     (ER_NO_LINENUMS_ADDED != err)) { 
	    goto CLEANUP_1;
	 }
      } else { /* valid symbol and linenum */
    /* Winky 12/3/97 */
    if (columnRange.columnEnd) {
       columnRange.columnStart = 0;
       columnRange.columnEnd = SYM_MAX_COLUMN;
    }
    /* EOF Winky 12/3/97 */
	 bkptTable[index].symMatch = TRUE;
	 bkptTable[index].module = moduleDesc;
	 bkptTable[index].function = functionDesc;  /* could be NULL_SYMBOL */
	 bkptTable[index].lineNumber = linenum;
	 bkptTable[index].columnRange = columnRange;
	 if (moduleDesc != NULL_SYMBOL) {
	    if (GOOD != (err = BxInsertBkptNamesAndOffset(index)))
	       goto CLEANUP_1;
	 }
      }
   } /* end of symbol stuffing */
NOSYMBOL_BKPT:   
   /* normal cleanup */
   err = AdrDestroyAddress(addrRangeDesc);  
   if (GOOD == firstErr) firstErr = err;
   
INTERNAL_BKPT:   
   /*----------------------*/
   /* fill in rest of data */
   /*----------------------*/
   bkptTable[index].statementTextPtr = TMalloc(1L);
   if (!bkptTable[index].statementTextPtr) {
      err = ER_OUT_OF_MEMORY;
      goto CLEANUP_1;
   }
   /* insert NULL; no text when set from asm */
   bkptTable[index].statementTextPtr[0] = '\0';

   /* this is placed later in this function so that it is not incremented
      if some function call errors out */
   if (bkptTable[index].type == BX_NOT_BEING_USED) {
      bkptCount++;   /* only increment count if new bkpt */
   }
   /*-----------------------------------------------------*/
   /* finally, set type which makes the breakpoint active */
   /*-----------------------------------------------------*/
   if (cliBkptNum == NOT_CLI) {
      U16 num;
      /* must get unused .cliBkptNumber and save, since all bkpts get one */
      if (GOOD != (err = CreateCliBkptNum(&num)))
	 goto CLEANUP_1;
      bkptTable[index].cliBkptNumber = num;
      bkptTable[index].type = BX_SET_FROM_ASM;
   } else {
      bkptTable[index].type = BX_SET_FROM_CLI;
      bkptTable[index].cliBkptNumber = cliBkptNum;  /* save value passed in */
   }
   /* override what was set if bkpt is a temp internal */
   if (life == BX_TEMPORARY_INTERNAL) {
      bkptTable[index].type = BX_SET_INTERNAL;
      bkptTempInternalCount++;
   }
   *desc = (DESCRIPTOR)index;

   if (bkptTable[index].state == BX_ENABLED) {  /* only program if enabled */
      if ((err = BkptProgramBkpt(index)) != GOOD) {
	 /* if failed, clear breakpoint */
	 BxRemoveBkpt(*desc);
	 return(err);
      }
      if (GOOD == firstErr) firstErr = err;
   }

   if (GOOD == firstErr) { /* only notify if all's well */
      if (bkptTable[index].life == BX_TEMPORARY_INTERNAL) {
	 return GOOD;   /* do not report to outside if temporary internal */
      } else {
	 return EnlEventNotify(EVENT_BKPT_EDIT);
      }
   } else {
      goto CLEANUP_2;  /* remove bkpt addr desc if error */
   }

CLEANUP_1:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(addrRangeDesc);
   if (GOOD == firstErr) firstErr = err;
   /* fall thru */

CLEANUP_2:  /* only if error occurred prior to saving addr desc */
   if (GOOD == firstErr) firstErr = err;
   // if breakpoint already cleaned up, address has already been destroyed
   if (firstErr != ER_TOO_MANY_XBRKS) {
      err = AdrDestroyAddress(bkptAddr);  /* remove passed-in addr */
      if (GOOD == firstErr) firstErr = err;
      bkptTable[index].addr = NULL;
   }
   return firstErr;
}  /* end of BxSetBkptWithIndex */


/*---------------------------------------------------------------------------
** BxInsertBkptNamesAndOffsets
**
** Purpose: given an index to a bkpt and the module and function descriptors
**    have been calculated, this function inserts the module reference name,
**    module name, function name.  It also calculates the offset from the
**    bkpt address to the start of the function.
----------------------------------------------------------------------------*/
RETCODE BxInsertBkptNamesAndOffset(BKPT_INDEX index)  {
   RETCODE      err, firstErr = GOOD;
   U16          bufLen;
   S8           buf[MAX_SYMNAME_LENGTH];
   DESCRIPTOR   tmpAddrDesc;
   ADDR_COMPARE result;
   BOOLEAN      dummy;  
	
   /* get names */
   /* places malloc'ed descriptor into bkpt element */
   firstErr = SymGetSymbolName(bkptTable[index].module,
			       &bkptTable[index].modName);
   if (bkptTable[index].function != NULL_SYMBOL) {                             
      err = SymGetSymbolName(bkptTable[index].function,
			     &bkptTable[index].functionName);
      if (GOOD == firstErr) firstErr = err;
   }   

   // allocate memory for the modRefName if there is module reference
   // else leave it as NULL
   bkptTable[index].modRefName = NULL_SYMBOL;
   err = SymGetModuleRef(bkptTable[index].module, &buf[0]);
   if (GOOD == firstErr) firstErr = err;

   if (!(bufLen = (U16)strlen(buf))) {
      buf[0] = '\0';
   } else {
      /* get space for mod ref name */
      bkptTable[index].modRefName = (DESCRIPTOR)TMalloc((U32)(bufLen + 1));
      if (!bkptTable[index].modRefName) {
	 return ER_OUT_OF_MEMORY;  /* priority over firstErr */
      }
      /* copy from tmp into TMalloc'ed memory */
      strcpy((LPSTR)(bkptTable[index].modRefName), &buf[0]);
   }
   
   if (GOOD != firstErr)
      return firstErr;  /* don't continue if already errs */

   //----------------------------------------------------------
   // calculate the bkpt address offset from start of function
   //----------------------------------------------------------

   // NOTES: Nghia - 07/27/94
   // Don't need to do anything else if there is no function symbol
   if (bkptTable[index].function == NULL) return firstErr;

   if (GOOD != (err = AdrDuplicateAddress(bkptTable[index].addr,
	       &tmpAddrDesc)))
      return err;
   // Get address descriptor of the start function
   if (GOOD != (err = SymGetSymbolAddress(bkptTable[index].function,
					  tmpAddrDesc)))
      goto CLEANUP;
   // Convert to ADDR_LINEAR 
   if (GOOD != (err = AdrConvertAddress(tmpAddrDesc, ADDR_LINEAR, 
	  (BOOLEAN FAR *) &dummy))) goto CLEANUP;

   // make sure that the breakpoint address is still in the function
   if ((GOOD != (err = AdrCompareAddresses(bkptTable[index].addr,
	  tmpAddrDesc, &result))) || (result == SECOND_ADDR_GREATER)) {
      err = (err != GOOD) ? err : ER_BX_BKPT_LESSTHAN_FUNC;
      goto CLEANUP;
   }

   /* Compute the offset from the start of the function */ 
   err = AdrRangeOfAddresses(tmpAddrDesc, bkptTable[index].addr, 
				  (U32 FAR *) &bkptTable[index].funcAddrOffset); 

CLEANUP:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(tmpAddrDesc);
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
}  /* end of BxInsertBkptNamesAndOffset */


/*---------------------------------------------------------------------------
**  BxClearBkpt
----------------------------------------------------------------------------*/
RETCODE BxClearBkpt(BKPT_INDEX index)  {

   RETCODE firstErr = GOOD;

   /* initialize bkpt offset from start of function */
   bkptTable[index].funcAddrOffset = 0L;

   /* if not a new bkpt, free memory and init before putting new values in */
   if (bkptTable[index].type != BX_NOT_BEING_USED) {
      firstErr = BxRemoveBkptAllocation(index);
   }
   return firstErr;
}  /* end of BxClearBkpt */


/*---------------------------------------------------------------------------
** BxRemoveBkptAllocation
**
** Frees up any memory allocated for a bkpt; includes address, names, and
**    strings
----------------------------------------------------------------------------*/
RETCODE BxRemoveBkptAllocation(BKPT_INDEX index)  {

   RETCODE err, firstErr = GOOD;

   if (bkptTable[index].addr)
      firstErr = AdrDestroyAddress(bkptTable[index].addr);

   if (NULL_SYMBOL != bkptTable[index].modRefName) {
      err = TFree((LPSTR)(bkptTable[index].modRefName));
      if (GOOD == firstErr)
	 firstErr = err;
      bkptTable[index].modRefName = 0L;
   }

   if (NULL_SYMBOL != bkptTable[index].modName) {
      err = TFree((LPSTR)(bkptTable[index].modName));
      if (GOOD == firstErr)
	 firstErr = err;
      bkptTable[index].modName = 0L;
   }

   if (NULL_SYMBOL != bkptTable[index].functionName) {
      err = TFree((LPSTR)(bkptTable[index].functionName));
      if (GOOD == firstErr)
	 firstErr = err;
      bkptTable[index].functionName = 0L;
   }

   if (0L != bkptTable[index].statementTextPtr) {
      err = TFree(bkptTable[index].statementTextPtr);
      if (GOOD == firstErr)
	 firstErr = err;
      bkptTable[index].statementTextPtr = 0L;
   }
   return firstErr;
}  /* end of BxRemoveBkptAllocation */


/*---------------------------------------------------------------------------
**  BxGetBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT BxGetBkpt(DESCRIPTOR desc,
			 BX_BPSTATE FAR *state,
			 BX_BPLIFE FAR *life,
			 DESCRIPTOR FAR *addr) {

   RETCODE err;

   if(bkptTable[(BKPT_INDEX)desc].type==BX_NOT_BEING_USED)
      return ER_BX_INACTIVE_BKPT;

   *state = bkptTable[(BKPT_INDEX)desc].state;
   *life = bkptTable[(BKPT_INDEX)desc].life;
   if (GOOD != (err =
      AdrDuplicateAddress(bkptTable[(BKPT_INDEX)desc].addr,
			  addr)))
      return err;
   return GOOD;
}

/*---------------------------------------------------------------------------
**  BxEnableBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT BxEnableBkpt(DESCRIPTOR desc) {

   RETCODE err;

   if ((BKPT_INDEX)desc >= MAX_NUM_BKPT)
      return ER_BX_INVALID_DESCRIPTOR;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD) return err;
   if (bkptTable[(BKPT_INDEX)desc].state == BX_DISABLED) {
      bkptTable[(BKPT_INDEX)desc].state = BX_ENABLED;
      if (GOOD != (err = BkptProgramBkpt((BKPT_INDEX)desc)))
	 return err;
      return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
}  /* end of BxEnableBkpt */


/*---------------------------------------------------------------------------
**  BxDisableBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT BxDisableBkpt(DESCRIPTOR desc) {

   RETCODE err;

   if ((BKPT_INDEX)desc >= MAX_NUM_BKPT)
      return ER_BX_INVALID_DESCRIPTOR;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
   if (bkptTable[(BKPT_INDEX)desc].state == BX_ENABLED) {
      bkptTable[(BKPT_INDEX)desc].state = BX_DISABLED;
      if (GOOD != (err = BkptProgramBkpt((BKPT_INDEX)desc)))
	 return err;
      return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
} /* end of BxDisableBkpt */


/*---------------------------------------------------------------------------
**  BxEnableAllBkpts
----------------------------------------------------------------------------*/
RETCODE EXPORT BxEnableAllBkpts(VOID) {

   RETCODE    err;
   BKPT_INDEX i;
   BKPT_INDEX bkptPassCount;
   BOOLEAN    bkptChanged = FALSE;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
   bkptPassCount = bkptCount;
   for (i=0; (bkptPassCount && (i<MAX_NUM_BKPT)); i++) {
      if (bkptTable[i].type != BX_NOT_BEING_USED) {
	 if (bkptTable[(BKPT_INDEX)i].state == BX_DISABLED) {
	    bkptTable[(BKPT_INDEX)i].state = BX_ENABLED;
	    if (GOOD != (err = BkptProgramBkpt((BKPT_INDEX)i)))
	       return err;
	    bkptChanged = TRUE;
	 }
      bkptPassCount--;
      }
   }
   if (bkptChanged) {
      return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
}  /* end of BxEnableAllBkpts */


/*---------------------------------------------------------------------------
**  BxDisableAllBkpts
----------------------------------------------------------------------------*/
RETCODE EXPORT BxDisableAllBkpts(VOID) {

   RETCODE    err;
   BKPT_INDEX i;
   BKPT_INDEX bkptPassCount;
   BOOLEAN    bkptChanged = FALSE;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
   bkptPassCount = bkptCount;
   for (i=0; (bkptPassCount && (i<MAX_NUM_BKPT)); i++) {
      if (bkptTable[i].type != BX_NOT_BEING_USED) {
	 if (bkptTable[(BKPT_INDEX)i].state == BX_ENABLED) {
	    bkptTable[(BKPT_INDEX)i].state = BX_DISABLED;
	    if (GOOD != (err = BkptProgramBkpt((BKPT_INDEX)i)))
	       return err;
	    bkptChanged = TRUE;
	 }
      bkptPassCount--;
      }
   }
   if (bkptChanged) {
      return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
}  /* end of BxDisableAllBkpts */


/*---------------------------------------------------------------------------
**  BxRemoveBkpt
----------------------------------------------------------------------------*/
RETCODE EXPORT BxRemoveBkpt(DESCRIPTOR desc) {

   RETCODE err, err1;
   BOOLEAN tempInternalBkpt = FALSE;

   if ((BKPT_INDEX)desc >= MAX_NUM_BKPT)
      return ER_BX_INVALID_DESCRIPTOR;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
   if (bkptTable[(BKPT_INDEX)desc].type != BX_NOT_BEING_USED) {
      if (BX_TEMPORARY_INTERNAL == bkptTable[(BKPT_INDEX)desc].life)
	 tempInternalBkpt = TRUE;
      bkptTable[(BKPT_INDEX)desc].type = BX_NOT_BEING_USED;
      if (bkptCount)
	 bkptCount--;

      if (bkptTable[(BKPT_INDEX)desc].state == BX_ENABLED) {
	 err  = BkptProgramBkpt((BKPT_INDEX)desc);
      }
      err1 = BxRemoveBkptAllocation((BKPT_INDEX)desc);
      if (err)
	 return err;
      if (err1)
	 return err1;
      if (FALSE == tempInternalBkpt)
	 return EnlEventNotify(EVENT_BKPT_EDIT);
   }
   return GOOD;
}  /* end of BxRemoveBkpt */


/*---------------------------------------------------------------------------
**  BxRemoveAllBkpts
----------------------------------------------------------------------------*/
RETCODE EXPORT BxRemoveAllBkpts(VOID) {

   RETCODE    err = GOOD, firstErr = GOOD;
   BKPT_INDEX i;
   BOOLEAN    anyBkptNotTempInternal = FALSE;

   if (!bkptCount)
      return GOOD;  /* no bkpts to remove */

   /* delete all valid bkpts; decrement bkptCount until 0 */

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;
   /* don't take any chances; go thru entire list, then set bkptCount to 0 */
   for (i=0; i < MAX_NUM_BKPT; i++) {
      if (bkptTable[i].type != BX_NOT_BEING_USED) {
	 bkptTable[i].type = BX_NOT_BEING_USED;
	 if (BX_TEMPORARY_INTERNAL != bkptTable[i].life)
	    anyBkptNotTempInternal = TRUE;
	 if (bkptTable[i].state == BX_ENABLED) {
	    err  = BkptProgramBkpt(i);
	 }
	 if (GOOD == firstErr)
	    firstErr = err;
	 err = BxRemoveBkptAllocation(i);  /* remove all alloc'ed bkpt data */
	 if (GOOD == firstErr)
	    firstErr = err;
      }
   }
   /* can only get here if there were some valid bkpts */
   /* defensive programming: make sure bkptCount = 0 */
   bkptCount = 0;
   if (anyBkptNotTempInternal)
      err = EnlEventNotify(EVENT_BKPT_EDIT);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxRemoveAllBkpts */


/*---------------------------------------------------------------------------
** BxGetBkptCount
**
** Design: bkptCount includes temporary internals; take out of return value
----------------------------------------------------------------------------*/
RETCODE EXPORT BxGetBkptCount(U16 *bkptCountParam) {

   *bkptCountParam = bkptCount - bkptTempInternalCount;
   return GOOD;
}


/*---------------------------------------------------------------------------
** BxRemoveAllInternalBkpts
**
** Purpose: delete all temporary internal bkpts
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxRemoveAllInternalBkpts(VOID) {

   RETCODE    err, firstErr = GOOD;
   BKPT_INDEX i, bkptPassCount;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   bkptPassCount = bkptCount;

   /* delete all temporary internal bkpts */

   for (i=0; (bkptPassCount && (i<MAX_NUM_BKPT)); i++) {
      if (bkptTable[i].type != BX_NOT_BEING_USED) {
	 bkptPassCount--;
	 if (bkptTable[i].life == BX_TEMPORARY_INTERNAL) {
	    bkptTable[i].type = BX_NOT_BEING_USED;
	    err = BkptProgramBkpt(i);
	    if (GOOD == firstErr)
	       firstErr = err;
	    /* remove all alloc'ed bkpt data */
	    err = BxRemoveBkptAllocation(i);
	    if (GOOD == firstErr)
	       firstErr = err;
	    bkptCount--;
	 }
      /* NOTE: event notification is not generated when internal bkpt */
      }
   }
   bkptTempInternalCount = 0;
   return firstErr;
}  /* end of BxRemoveAllInternalBkpts */


/***************************************************************************
**
** BxRemoveMatchingTemporary
**
** Purpose: if the currentPC matches a temporary bkpt, remove it
**
** Output parameter:
**    tempMatch: TRUE if a match was found; FALSE otherwise
**
** NOTE: it is possible to have more than one temporary bkpt with the same
**       address.  This routine finds the first one in the array and removes
**       it.  It does not search any further.
*****************************************************************************/
RETCODE PRIVATE
BxRemoveMatchingTemporary(BOOLEAN *tempMatch) {

   RETCODE      err, firstErr = GOOD;
   BKPT_INDEX   i, bkptPassCount;
   DESCRIPTOR   currentPCDesc;
   ADDR_COMPARE compareResult;

   *tempMatch = FALSE;
   if (!bkptCount)
      return GOOD;  /* no work if no bkpts */
   bkptPassCount = bkptCount;
   if (GOOD != (err = CpuGetPC(&currentPCDesc)))
      return err;
   /* set to invalid value to distinguish from matching bkpt */
   for (i=0; (bkptPassCount && (i<MAX_NUM_BKPT)); i++) {
      bkptPassCount--;
      if ((bkptTable[i].type != BX_NOT_BEING_USED) &&
	 (bkptTable[i].life == BX_TEMPORARY) &&
	 (AdrComparePhysicalAddresses(currentPCDesc,
		      bkptTable[i].addr, &compareResult) == GOOD)) {
	 if (EQUAL_ADDRS == compareResult) {
	    firstErr = BxRemoveBkpt(i);
	    *tempMatch = TRUE;
	    break;
	 }
      }
   }
   /* remove the unused currentPCDesc */
   err = AdrDestroyAddress(currentPCDesc);
   return (firstErr != GOOD) ? firstErr : err;
   
}  /* end of BxRemoveMatchingTemporary */


/*---------------------------------------------------------------------------
** BxBkptMatchInternal
**
** Purpose: if the currentPC matches any temporary internal bkpt, report it
**
** Input parameter:
**    matchAddr: address to match with bkpts set
**
** Output parameter:
**    match: TRUE if a temporary internal bkpt address matches input addr
**           FALSE if none match
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxBkptMatchInternal(DESCRIPTOR matchAddr, BOOLEAN *match) {
   BKPT_INDEX   i, bkptPassCount;
   ADDR_COMPARE compareResult;

   *match = FALSE;
   if (!bkptCount)
      return GOOD;  /* no work if no bkpts */
   bkptPassCount = bkptCount;

   for (i=0; (bkptPassCount && (i<MAX_NUM_BKPT)); i++) {
      bkptPassCount--;
      if ((bkptTable[i].type != BX_NOT_BEING_USED) &&
	 (bkptTable[i].life == BX_TEMPORARY_INTERNAL) &&
	 (AdrComparePhysicalAddresses(matchAddr, bkptTable[i].addr,
				   &compareResult) == GOOD)) {
	  /* until a match is found */  
	  if (EQUAL_ADDRS == compareResult) {
	     *match = TRUE;
	     return GOOD;
	  }
      }
   }
   return GOOD;
} /* end of BxBkptMatchInternal */

/*---------------------------------------------------------------------------
**  BkptProgramBkpt
**
**  NOTE: event notification must be done by caller directly or level(s) above
**        caller
----------------------------------------------------------------------------*/
RETCODE BkptProgramBkpt(BKPT_INDEX index) {

   U32        bkptAddr;
   U16        bkptId;
   BKPT_CMD   bkptCmd;
   ADDR_SPACE bkptSpace;
   BOOLEAN    timedOut;
   RETCODE    err;
   U16 segment;
   U32 seg;
   ADDR_SEGSEL_TYPE segType;

   if ((bkptTable[index].type == BX_NOT_BEING_USED) ||
       (bkptTable[index].state == BX_DISABLED))  {
      bkptCmd = BKPT_CLEAR;
   } else {
      bkptCmd = BKPT_SET;
   }
   bkptId = (U16)index;

   // Get the linear offset of breakpoint address and set
   if((err = AdrGetAddrOffset(bkptTable[index].addr,             
			      (U32 FAR *)&bkptAddr))!=GOOD)
      return err;
   AdrGetAddrSegmentSelector(bkptTable[index].addr, &segType, &segment);
   seg = (U32)segment;
   bkptAddr -= seg*16;
   bkptAddr += seg << 16;

   /* remove processor dependency */
   if (FAMILY_68K == staticProcFamily) {
      if((err=AdrGetAddrSpace(bkptTable[index].addr,
			      (ADDR_SPACE FAR *)&bkptSpace))!=GOOD)
	 return err;
   }
   /*---------------------------------
   ** send the breakpoint information
   -----------------------------------*/
   if((err = SdWriteMember(bkAddrDesc, (U8 FAR *)&bkptAddr, GOOD))!=GOOD)
      return err;
   if (FAMILY_68K == staticProcFamily) {
      if((err = SdWriteMember(bkSpaceDesc, (U8 FAR *)&bkptSpace, GOOD))!=GOOD)
	 return err;
   }
   if((err = SdWriteMember(bkIdDesc, (U8 FAR *)&bkptId, GOOD))!=GOOD)
      return err;
   if((err = SdWriteCmdReadResponse(bkCmdDesc, (U8 FAR *)&bkptCmd, GOOD,
      bkResultDesc, 0L, &timedOut))!=GOOD)
      return err;
   return GOOD;
}

/*---------------------------------------------------------------------------
**  GetAvailableBkpt
----------------------------------------------------------------------------*/
RETCODE GetAvailableBkpt(BKPT_INDEX *index) {

   for((*index)=0; (*index)<MAX_NUM_BKPT; (*index)++) {
      if(bkptTable[*index].type == BX_NOT_BEING_USED)
	 return GOOD;
   }
   return ER_BX_NO_BKPT_AVAILABLE ;
}

/*---------------------------------------------------------------------------
**  AddrHasNoBkpt
----------------------------------------------------------------------------*/
RETCODE AddrHasNoBkpt(DESCRIPTOR addrDesc, BX_BPLIFE life) {
   BKPT_INDEX index, bkptPassCount;
   ADDR_COMPARE result;
   S8 tmpStr[ADDR_BUFF_SZ+30];
   RETCODE err;

   // This does not check for duplication if the requested breakpoint is
   // a temporary internal breakpoint
   if (life == BX_TEMPORARY_INTERNAL) {
      return GOOD;
   }
   // Check up to the number of set breakpoint or max number of breakpoints
   bkptPassCount = bkptCount;
   for (index = 0; (bkptPassCount && index < MAX_NUM_BKPT); index++) {
      --bkptPassCount;
      if ((bkptTable[index].type != BX_NOT_BEING_USED) &&
	 (bkptTable[index].life != BX_TEMPORARY_INTERNAL) &&
	 (AdrComparePhysicalAddresses(bkptTable[index].addr, addrDesc,
			  (ADDR_COMPARE FAR *) &result) == GOOD)) {
	 if (result == EQUAL_ADDRS) {
	    S8 addrBuf[ADDR_BUFF_SZ];
	    if ((err = AdrConvAddressToTextWithParams(bkptTable[index].addr,
		    TRUE, FALSE, (LPSTR)addrBuf)) !=GOOD)
	       return(err);
	    /* Format the string message */
	    sprintf(tmpStr, "address = %s, bkpt id = @%d.", addrBuf,
	       bkptTable[index].cliBkptNumber);
	    ErrDisplayString(ER_BX_ALREADY_HAS_BKPT, (LPSTR)tmpStr, CHECK_MODE);
	    return(ER_BX_ALREADY_HAS_BKPT);
	 }
      }
   }
   return GOOD;
}

/*---------------------------------------------------------------------------
**  BxGetCauseOfLastBreak
----------------------------------------------------------------------------*/
#pragma argsused
RETCODE EXPORT BxGetCauseOfLastBreak(BREAK_CAUSE FAR *cause) {

   return SdnReadMember(SDN_BREAK_CAUSE, (U8 FAR *)cause);
}

/*---------------------------------------------------------------------------
** BxGetSrcAddressRange
**
** Purpose: Get the address range of the line/stmt that matches the current
**          PC.  Uses caching of linenum index to speed up process.
**
** Input parameters:
**   currentPC: address descriptor of current Program Counter
**   granularity: step by stmt or line
**
** Output parameters:
**    rangeDesc: address range of line/stmt found; created here, must be
**               destroyed by caller.
**               If error, address descriptor destroyed.
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGetSrcAddressRange(DESCRIPTOR     currentPC,
		     BX_GRANULARITY granularity,
		     DESCRIPTOR FAR *rangeDesc) {

   RETCODE            err, firstErr = GOOD;
   LINENUM_DESCRIPTOR nextLinenumIndex;
   LINENUM_TYPE       linenum;
   COLUMN_TYPE        column;
   COLUMN_RANGE_TYPE  columnRange;
   SYM_DESCRIPTOR     module;
   BOOLEAN            isInRange;
   DESCRIPTOR         moduleRangeDesc; 

   /* Need to duplicate the currentPC to preserve address information */
   if (GOOD != (err = AdrDuplicateAddress(currentPC, rangeDesc)))
      return err;
   if (GOOD != (err = AdrSetRangeActive(*rangeDesc, TRUE)))
      goto CLEANUP;
   if ((lookaheadModule) && (!lookaheadLinenumIndex)) {
      if (granularity == BX_LINE_GRAN) {
	 err = SymGetLinenumByIndex(lookaheadModule,
				    lookaheadLinenumIndex,
				    *rangeDesc, /* addr desc to fill in */
				    &nextLinenumIndex,
				    &linenum,
				    &column);

      } else {
	 err = SymGetLinenumStmtByIndex(lookaheadModule,
					lookaheadLinenumIndex,
					*rangeDesc,
					&linenum,
					&columnRange,
					&nextLinenumIndex);
      }
      if ((err != GOOD) && (err != ER_LINENUM_INDEX_TOO_LARGE))
	 goto CLEANUP;
      lookaheadLinenumIndex = nextLinenumIndex; /* save for next time */

      /* now see if requested address within address range of linenum/stmt */
      if (GOOD != (err = AdrIsAddrInRange(currentPC, *rangeDesc, &isInRange)))
	 goto CLEANUP;
      if (isInRange)
	 return GOOD;
      /* otherwise fall thru and use the SymMapAddr2Linenum... to get linenum
	 address range */
   }
   /* find module, linenum, column match for requested address */
   if (GOOD != (err = AdrDuplicateAddress(currentPC, &moduleRangeDesc)))
      return err;
   if (granularity == BX_LINE_GRAN) {
      err = SymMapAddr2LinenumModule(currentPC,
				     &module,
				     moduleRangeDesc, /* module addr range */
				     &linenum,
				     &column,
				     *rangeDesc, /* addr desc to fill in */
				     &nextLinenumIndex);
   } else { /* stmt */
      err =
	 SymMapAddr2LinenumStmtModule(currentPC,
				      &module,
				      moduleRangeDesc,/* module addr range */
				      &linenum,
				      &columnRange,
				      *rangeDesc, /* addr desc to fill in */
				      &nextLinenumIndex);
   }
   firstErr = err;
   err = AdrDestroyAddress(moduleRangeDesc);
   if (GOOD == firstErr) firstErr = err;
   if (GOOD != firstErr)
       goto CLEANUP;
   lookaheadModule = module;  /* save possibly new module */
   lookaheadLinenumIndex = nextLinenumIndex; /* save for next time */
   return GOOD;

CLEANUP:  /* only if an error */
   if (GOOD == firstErr)
      firstErr = err;
   AdrDestroyAddress(*rangeDesc);
   return firstErr;
} /* end of BxGetSrcAddressRange */

/*---------------------------------------------------------------------------
**
** BxSrcStep
**
** Do <count> source-level single steps.  This is a root function that
** does common setup and cleanup, then calls BxSrcStepFast and BxSrcStepSlow.
** The fast step function uses potentially lots of breakpoints which can
** cause failure if program memory is not writable.  If the ER_TOO_MANY_XBRKS
** error is returned, plan B (BxSrcStepSlow) is used.  This needs lots of
** emulation sessions to perform the source step, but uses at most one
** breakpoint.
**
---------------------------------------------------------------------------*/
RETCODE EXPORT BxSrcStep(BX_STEP_TYPE type, BX_GRANULARITY gran, U32 count) {
   RETCODE err,err2;
   DESCRIPTOR pcAddrDesc, srcRangeDesc, tmpAddrDesc;
   BOOLEAN   abortFromEsc;


   if ((err = BkProcessorMustBeHaltedAbsolute()) != GOOD) return(err);
   if (count == 0) return(ER_BX_INVALID_COUNT);
   if (gran == BX_ASM_GRAN) return (ER_BX_ASMGRANULARITY_INVALID);
   if ((err = CpuGetPC(&pcAddrDesc)) != GOOD) return(err);
   if ((err = BxGetSrcAddressRange(pcAddrDesc, gran, &srcRangeDesc)) != GOOD){
      AdrDestroyAddress(pcAddrDesc);
      return(err);
   }
   if ((err = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED)) != GOOD) {
      AdrDestroyAddress(pcAddrDesc);
      AdrDestroyAddress(srcRangeDesc);
      return(err);
   }
   /* clear any ESC previously hit */
   err = TskCheckAbort(&abortFromEsc);
   if(err!=GOOD) {
      AdrDestroyAddress(pcAddrDesc);
      AdrDestroyAddress(srcRangeDesc);
      return err;
   }
   tmpAddrDesc=pcAddrDesc;   /*<Judy 2/3/1997> */
   while (count--) {
      err=BxSrcStepFast(type,gran,&pcAddrDesc,&srcRangeDesc);
      BxRemoveAllInternalBkpts();
      if (err == ER_TOO_MANY_XBRKS) {
	 err=BxSrcStepSlow(type,gran,&pcAddrDesc,&srcRangeDesc);
	 BxRemoveAllInternalBkpts();
      }
      if (err != GOOD) break;
   }
   pcAddrDesc=tmpAddrDesc;   /*<Judy 2/3/1997> */
   err2 = EnlEventNotify(EVENT_BKPT_HALTED);
   if (!err) err = err2;
   err2 = AdrDestroyAddress(pcAddrDesc);
   if (!err) err = err2;
   if (srcRangeDesc) {
      err2 = AdrDestroyAddress(srcRangeDesc);
      if (!err) err = err2;
   }
   return(err);
}

/*---------------------------------------------------------------------------
** BxSrcStepFast
**
** Do one source level single step.  Uses a fast algorithm that might
** require several breakpoints.  If memory is not writable, this algorithm
** may fail because hardware breakpoints are limited.
**
** while (1) {
**    disassemble range of source line/stmt
**    for each transfer instruction,
**       if (call instruction) {
**          if (step-into mode) {
**             if (destination is known)
**                if (destination is symbolic) set bkpt at destination address
**             else
**                set bkpt at call instruction
**          }
**       } else if (dest known) {
**          if (dest outside source line range)
**             set bkpt at destination address
**       } else
**          set bkpt at transfer instruction
**    set bkpt at instruction just past end of source line
**    while (1) {
**       go
**       wait for break
**       if (cause != temp bkpt) remove bkpts; done
**       if (PC out of line range) break;
**       step
**       if (cause != step) remove bkpts; done
**       if (PC out of line range) break;
**    }
**    remove bkpts
**    if (PC == starting address of new source line) done
** }
**
---------------------------------------------------------------------------*/
RETCODE PRIVATE BxSrcStepFast(BX_STEP_TYPE type, BX_GRANULARITY gran,
      DESCRIPTOR *pcAddrDesc, DESCRIPTOR *srcRangeDesc) {
   RETCODE err;
   BOOLEAN abortFromEsc = FALSE;
   U32 srcStart, srcEnd;

   Sds2AbiFwUnloadRegisters(TRUE);
   if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
   while (1) {
      ADDR_SEGSEL_TYPE segType;
      U16 seg;
      U32 addrSeg;
      BOOLEAN inRange;
      if ((err = DadSetDasmAddr(dasmID, *pcAddrDesc)) != GOOD) return(err);

      while (2) {
	 DESCRIPTOR bkptDesc, transferAddrDesc;
	 TRANSFER_TYPE transferType;

	 if (GOOD != (err = TskCheckAbort(&abortFromEsc)))
	    return err;
	 if (abortFromEsc)
	    return(ER_ABORT_FROM_ESC);

	 if ((err = AdrDuplicateAddress(*pcAddrDesc, &transferAddrDesc))
	    != GOOD) return(err);
	 if ((err = DadSetDasmAddr(dasmID, *pcAddrDesc)) != GOOD) return(err);
	 if ((err = DadDasmIsTransfer(dasmID, transferAddrDesc, &transferType))
	    != GOOD) {
	    if (transferAddrDesc) AdrDestroyAddress(transferAddrDesc);
	    return(err);
	 }
	 if (*pcAddrDesc) {
	    if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
	 }
	 if ((err = DadGetDasmAddr(dasmID, pcAddrDesc)) != GOOD) return(err);
	 if (transferType.transfer) {
	    if (transferType.call) {
	       BOOLEAN isFunc;
	       if ((err = BxIsFunction(transferAddrDesc, &isFunc)) != GOOD)
		  return(err);
	       if (!isFunc || type == BX_STEP_OVER) {
		  AdrGetAddrOffset(*pcAddrDesc, &srcStart);
		  AdrGetAddrSegmentSelector(*pcAddrDesc, &segType, &seg);
		  addrSeg = (U32)seg;
		  srcStart += addrSeg << 16;
		  if (iceRunUntil(srcStart) == ICE_USER_HALT) {
		     if (transferAddrDesc) {
			if ((err = AdrDestroyAddress(transferAddrDesc)) != GOOD)
			   return(err);
		     }
		     Sds2AbiFwUnloadRegisters(TRUE);
		     return(ER_ABORT_FROM_ESC);
		  }
	       }
	    } else { /* JMP or conditional JMP */
//               if (type == BX_STEP_OVER) {
		  AdrGetAddrOffset(*srcRangeDesc, &srcStart);
		  AdrGetAddrOffset(*pcAddrDesc, &srcEnd);
		  AdrGetAddrSegmentSelector(*srcRangeDesc, &segType, &seg);
		  addrSeg = (U32)seg;
		  srcStart += addrSeg << 16;
		  srcEnd += addrSeg << 16;
		  iceStepRange(srcStart, srcEnd-1);
		  Sds2AbiFwUnloadRegisters(TRUE);
		  if (*pcAddrDesc) {
		     if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) 
			return(err);
		  }
		  if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
//               }
	    }
	 }
	 if (transferAddrDesc) {
	    if ((err = AdrDestroyAddress(transferAddrDesc)) != GOOD)
	       return(err);
	 }
	 if ((err = AdrIsAddrInRange(*pcAddrDesc, *srcRangeDesc, &inRange))
	    != GOOD) return(err);
	 if (!inRange) break;
      } /* while (2) */

      /*
      ** step until PC is outside source range
      */
      Sds2AbiFwUnloadRegisters(TRUE);
      if (*pcAddrDesc) {
	 if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
      }
      if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
      if ((err = AdrIsAddrInRange(*pcAddrDesc, *srcRangeDesc, &inRange))
	 != GOOD) return(err);
      if (inRange) {
	 AdrGetAddrOffset(*srcRangeDesc, &srcStart);
	 AdrGetEndAddrOffset(*srcRangeDesc, &srcEnd);
	 AdrGetAddrSegmentSelector(*srcRangeDesc, &segType, &seg);
	 addrSeg = (U32)seg;
	 srcStart += addrSeg << 16;
	 srcEnd += addrSeg << 16;
	 iceStepRange(srcStart, srcEnd);
      }
      Sds2AbiFwUnloadRegisters(TRUE);
      if (*pcAddrDesc) {
	 if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
      }
      if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
      
      /*
      ** If PC is at starting address of new source line, done
      */
      {
	 ADDR_COMPARE result;
	 if (*srcRangeDesc) {
	    if ((err = AdrDestroyAddress(*srcRangeDesc)) != GOOD) return(err);
	 }
	 if ((err = BxGetSrcAddressRange(*pcAddrDesc,gran,srcRangeDesc))
	    != GOOD) {
	    *srcRangeDesc = NULL;
	    break;   /* err set when pc is not in any source module */
	 }
	 if ((err = AdrCompareAddresses(*srcRangeDesc, *pcAddrDesc, &result))
	    != GOOD) return(err);
         if (result == EQUAL_ADDRS) {
            /* <Judy 2/3/1997> */
            if (*pcAddrDesc) {
               if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
            }
            /* <Judy 2/3/1997> */
         }
         break;
      }
   } /* while (1) */
   return(GOOD);
}

/*---------------------------------------------------------------------------
** BxSrcStepSlow
**
** Do one source level single step.  Uses a slower algorithm that
** requires a maximum of 1 breakpoint.
**
** while (1) {
**    disassemble instruction at PC
**    if (call instruction AND (step-over mode OR dest is not a function))
**       set bkpt at address after call
**       go
**       wait for break
**       if (cause != temp bkpt) done
**       remove temp bkpts
**    else
**       step
**    if (PC == starting address of new source line) done
** }
**
---------------------------------------------------------------------------*/
RETCODE PRIVATE BxSrcStepSlow(BX_STEP_TYPE type, BX_GRANULARITY gran,
      DESCRIPTOR *pcAddrDesc, DESCRIPTOR *srcRangeDesc) {
   RETCODE err;
   BOOLEAN abortFromEsc;

   while (1) {
      TRANSFER_TYPE transferType;
      BOOLEAN inRange, bkptMatch, bkptUsed;
      DESCRIPTOR nextAddrDesc, bkptDesc;

      if ((err = TskCheckAbort(&abortFromEsc)) != GOOD)
	 return err;
      if (abortFromEsc)
	 return ER_ABORT_FROM_ESC;

      if ((err = DadSetDasmAddr(dasmID, *pcAddrDesc)) != GOOD) return(err);
      if ((err = DadDasmIsTransfer(dasmID, *pcAddrDesc, &transferType))
	 != GOOD) return(err);
      bkptUsed=FALSE;
      if (transferType.transfer && transferType.call) {
	 BOOLEAN isFunc=FALSE;
	 if ((type == BX_STEP_INTO) && transferType.destKnown) {
	    if ((err = BxIsFunction(*pcAddrDesc, &isFunc)) != GOOD)
	       return(err);
	 }
	 if (!isFunc || (type == BX_STEP_OVER)) {
	    /* set bkpt on instruction after call */
	    if ((err = DadGetDasmAddr(dasmID, &nextAddrDesc)) != GOOD)
	       return(err);
	    if ((err = BxSetBkpt(BX_ENABLED,BX_TEMPORARY_INTERNAL,
	       nextAddrDesc, &bkptDesc)) != GOOD) return(err);
	    bkptUsed=TRUE;
	    if (!demonstrationVersion) {
	       if ((err = BxMLStartEmulation(BX_GO_STEP))!=GOOD) return(err);
	       if ((err = BxWaitForEmulationHalt()) != GOOD) return(err);
	    } else {
	       if ((err = BxStepUntilBkpt()) != GOOD) return(err);
	    }
	    if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
	    if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
	    if ((err = BxBkptMatchInternal(*pcAddrDesc, &bkptMatch)) != GOOD)
	       return(err);
	    BxRemoveAllInternalBkpts();
	    if (!bkptMatch) return(GOOD);
	 }
      }
      if (!bkptUsed) {         /* no GO done above...do step */
	 if ((err = BxLLAsmStepInto(1L)) != GOOD) return(err);
	 if ((err = AdrDestroyAddress(*pcAddrDesc)) != GOOD) return(err);
	 if ((err = CpuGetPC(pcAddrDesc)) != GOOD) return(err);
      }
      if ((err = AdrIsAddrInRange(*pcAddrDesc, *srcRangeDesc, &inRange))
	  != GOOD) return(err);
      if (inRange) continue;   /* still in original source range */
      /*
      ** If PC is at starting address of new source line, done
      */
      {
	 ADDR_COMPARE result;
	 if ((err = AdrDestroyAddress(*srcRangeDesc)) != GOOD) return(err);
	 if ((err = BxGetSrcAddressRange(*pcAddrDesc,gran,srcRangeDesc))
	    != GOOD) {
	    *srcRangeDesc = NULL;
	    break;
	 }
	 if ((err = AdrCompareAddresses(*srcRangeDesc, *pcAddrDesc, &result))
	    != GOOD) return(err);
	 if (result == EQUAL_ADDRS) break;
      }
   }
   return(GOOD);
}

/*----------------------------------------------------------------------------
** BxIsFunction
**
** Used in step algorithm to determine if source is available for call
** transfer address.
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxIsFunction(DESCRIPTOR addrDesc, BOOLEAN *isFunc) {
   RETCODE err;
   MEM_ADDR_CLASS memoryClass;
   SYM_TYPE_TYPE symbolType;
   SYM_DESCRIPTOR outputSymbol, funcDescriptor, moduleDescriptor;
   U32 offset;
   *isFunc = FALSE;

   err = SymMapAddr2Symbol(addrDesc, &memoryClass, &symbolType, &offset,
      &outputSymbol, &funcDescriptor, &moduleDescriptor);
   if (err == ER_ADDRESS_NOT_FOUND) {
      *isFunc = FALSE;
      return(GOOD);
   } else if (err != GOOD) return(err);
   *isFunc = (funcDescriptor != NULL_SYMBOL);
   return(GOOD);
}

/*---------------------------------------------------------------------------
** BxGoUntilCallReturn
**
** Purpose: high level call to do go until either a call/return or return only
----------------------------------------------------------------------------*/
RETCODE EXPORT
BxGoUntilCallReturn(BX_GRANULARITY      gran,
		    BX_CALL_RETURN_TYPE callReturnType) {

   RETCODE err, firstErr;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   /* set up for go until */
   if (GOOD != (err = BxLocalGoUntilCallReturn(gran, callReturnType))) {
      BxRemoveAllInternalBkpts();
      return err;
   }

   /* set up for the callback */
   granState = gran;  /* needed after emu halted */

   if (demonstrationVersion) {
      if (GOOD != (err = BxStepUntilBkpt())) {
	 BxRemoveAllInternalBkpts();
	 return err;
      }
   } else {
      /* start emulation */
      if (GOOD != (err = BxMLStartEmulation(BX_GO_UNTIL))) {
	 BxRemoveAllInternalBkpts();
	 return err;
      }
   }

   firstErr = BxWaitForEmulationHalt();  /* spins on emuRunningSpinFlag */
   err = BxGoUntilCallReturnPostBkpt();  /* sends out _EMUL_HALTED */
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
} /* end of BxGoUntilCallReturn */


/*---------------------------------------------------------------------------
** BxLocalGoUntilCallReturn
**
** Purpose:
**    Implement algorithm except for setting state and granularity status
**
** Discussion:
**    algorithm is to disassemble from start to end of function looking for
**    function calls and returns.  If found, a bkpt is set at the beginning
**    of the line/stmt/instruction depending on the mode.  Once set, the
**    rest of the line/stmt range is ignored.  However, if the subroutine
**    call is not a function according to the symbol table, it is ignored
**    and executed over as if it was inline code.
**
** Design:
**    while (TRUE)
**       disassemble next instruction
**       if (call AND call/return mode)
**          if (destination == function)
**             set validCall flag
**          else clear validCall flag
**       if Return OR (validCall flag)
**          if line/stmt granularity
**             if addr NOT inside present line/stmt range
**                get new line/stmt range containing present address
**                set FALSE "already-set" flag
**             if "bkpt-already-set" flag FALSE then
**                set bkpt on first line/stmt range addr
**                set "already-set" flag
**                endif
**          else (asm granularity)
**             set bkpt on dasm addr
**       get next address
**       if (not in function range) break;
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxLocalGoUntilCallReturn(BX_GRANULARITY      gran,
			 BX_CALL_RETURN_TYPE callReturnType) {

   RETCODE err = GOOD, firstErr = GOOD;
   SYM_DESCRIPTOR currentFunc;
   DESCRIPTOR     funcAddrDesc, dasmAddrDesc, linenumAddrDesc,
		  bkptAddrRangeDesc, dummyAddrDesc;
   FUNC_CLASS     funcClass;
   U32            stackSize;
   BOOLEAN        isValidCall, isInRange, bkptAlreadySet = FALSE;
   DESCRIPTOR     bkptDesc;   /* return value from setting bkpt */
   U16            localBkptCount = 0;
   CALL_TYPE      instrType;

#ifdef DEBUG
/* !!! test vars */
   U32 tStartAddr, tEndAddr, tPCAddr, tBkptAddr;
   err = BxSendCliMessage((LPSTR)"start of local gountil");
   err = AdrCliDebug("", 1L, 0L);
#endif

   /* !!! check that the event notification order guarantees this is the
      latest value */
   if (GOOD != (err = StkGetCurrentFunction(&currentFunc)))
      return err;
   if (NULL_SYMBOL == currentFunc)
      return ER_SYM_NO_FUNCTION_MATCH;
   /* Use the current PC instead of the default address descriptor */
   if (GOOD != (err = CpuGetPC(&funcAddrDesc)))  return err;
   if (GOOD != (err = AdrDuplicateAddress(funcAddrDesc, &dummyAddrDesc)))
      goto CLEANUP_4;
   if (GOOD != (err = AdrSetRangeActive(funcAddrDesc, TRUE)))
      goto CLEANUP_3;

   firstErr = SymGetFunc(currentFunc,
			 (LPSTR)dummyNameStr,  /* name not used */
			 &funcClass,
			 &stackSize,
			 funcAddrDesc);

   if (firstErr != GOOD) goto CLEANUP_3;
   
#ifdef DEBUG
/* !!! test code */
      err = AdrGetAddrOffset(funcAddrDesc, &tStartAddr);
      err = AdrGetEndAddrOffset(funcAddrDesc, &tEndAddr);
#endif

   /* start disassembling at beginning of function */
   if (GOOD != (err = AdrDuplicateAddress(funcAddrDesc,
					  &dasmAddrDesc)))
       goto CLEANUP_3;

   /* init the linenum range */
   if (GOOD != (err = BxGetSrcAddressRange(dasmAddrDesc,
					   gran,
					   &linenumAddrDesc))) {
      // linenumAddrDesc is already destroyed
      AdrDestroyAddress(dasmAddrDesc);
      goto CLEANUP_3;
   }
   
   /* set up start address of disassembly; does NOT consume addr */
   if (GOOD != (err = DadSetDasmAddr(dasmID, dasmAddrDesc)))
      goto CLEANUP_1;

   /*-----------------------------------------------------------------------
   ** find call/return instructions, set breakpoints on either linenum, stmt,
   ** or instruction boundaries
   -------------------------------------------------------------------------*/
   /* linenumAddrDesc has an offset of 0 and range of 1 byte (so range
      comparison will work) */
   while (TRUE) {
      isValidCall = FALSE;  // init
      /* disassemble instruction */
      if (GOOD != (err =
	 DadDasmIsCallOrRTS(dasmID, dummyAddrDesc, &instrType)))
	 goto CLEANUP_1;

      if ((instrType == INST_CALL) && (callReturnType == BX_CALL_RETURN)) {
	 /* see if destination address is valid function;
	    if not, don't set bkpt on */
	 MEM_ADDR_CLASS memoryClass;
	 SYM_TYPE_TYPE  symbolType;
	 SYM_DESCRIPTOR outputSymbol, funcDescriptor, moduleDescriptor;
	 U32            offset;

	 if (GOOD != (err = SymMapAddr2Symbol(dummyAddrDesc,
					      &memoryClass,
					      &symbolType,
					      &offset,
					      &outputSymbol,
					      &funcDescriptor,
					      &moduleDescriptor))) {
	    goto CLEANUP_1;
	 }
	 if (funcDescriptor != NULL_SYMBOL) {  /* function found */
	    isValidCall = TRUE;
	 }
      }
      if (isValidCall || (instrType == INST_RETURN)) {
	 if (BX_ASM_GRAN == gran) {

	    /* dup the disasm address and set bkpt on it */
	    if (GOOD != (err =
	       AdrDuplicateAddress(dasmAddrDesc, &bkptAddrRangeDesc)))
	       goto CLEANUP_1;
	    /* remove processor dependency */
	    if (FAMILY_68K == staticProcFamily) {
	       if(GOOD != (err = AdrSetAddrSpace(bkptAddrRangeDesc, SPACE_SP)))
		  return err;
	    }
	    if (GOOD != (err =
	       BxSetBkpt(BX_ENABLED, BX_TEMPORARY_INTERNAL,
			 bkptAddrRangeDesc, &bkptDesc)))  { /* consumes addr */
	       goto CLEANUP_1;
	    }
	    localBkptCount++;

	 } else { /* line or stmt gran */
	    /* !!! insert test for destination with source */
	    if (GOOD != (err =
	       AdrIsAddrInRange(dasmAddrDesc, linenumAddrDesc, &isInRange)))
		  goto CLEANUP_1;
	    if (!isInRange) {  /* outside linenum range; get new one */
	       /* at this point linenumAddrDesc is valid; must destroy and
		  get new one from BxGetSrcAddressRange */
	       if (GOOD != (err = AdrDestroyAddress(linenumAddrDesc)))
		  goto CLEANUP_1;
	       if (GOOD != (err =
		  BxGetSrcAddressRange(dasmAddrDesc,
				       gran,
				       &linenumAddrDesc))) { /* creates addr */
		  // linenumAddrDesc is already destroyed
		  AdrDestroyAddress(dasmAddrDesc);
		  goto CLEANUP_3;
	       }
#ifdef DEBUG
/* !!! test code */
	       err = AdrGetAddrOffset(linenumAddrDesc, &tStartAddr);
	       err = AdrGetEndAddrOffset(linenumAddrDesc, &tEndAddr);
#endif
	       bkptAlreadySet = FALSE;
	    }
	    if (!bkptAlreadySet) {
	       if (GOOD != (err = AdrDuplicateAddress(linenumAddrDesc,
						      &bkptAddrRangeDesc)))
		  goto CLEANUP_1;
	       /* remove processor dependency */
	       if (FAMILY_68K == staticProcFamily) {
		  if (GOOD != (err =
			       AdrSetAddrSpace(bkptAddrRangeDesc, SPACE_SP))) {
		     AdrDestroyAddress(bkptAddrRangeDesc);
		     goto CLEANUP_1;
		  }
	       }
	       if (GOOD != (err =
		  BxSetBkpt(BX_ENABLED, BX_TEMPORARY_INTERNAL,
			    bkptAddrRangeDesc, &bkptDesc))) {  /* saves addr */
		  goto CLEANUP_1;
	       }

#ifdef DEBUG
/* !!! test code */
	       err = AdrGetAddrOffset(bkptAddrRangeDesc, &tBkptAddr);
#endif
	       bkptAlreadySet = TRUE;
	       localBkptCount++;
	    }
	 } /* end of gran if stmt */
      } /* end of if (instrType == INST_CALL) */

      /* get rid of previous addr copy */
      if (GOOD != (err = AdrDestroyAddress(dasmAddrDesc)))
	 goto CLEANUP_2;

      /* check to see if still in the function range (produces new addr) */
      if (GOOD != (err = DadGetDasmAddr(dasmID, &dasmAddrDesc)))
	 goto CLEANUP_2;

      if (GOOD != (err =
	 AdrIsAddrInRange(dasmAddrDesc, funcAddrDesc, &isInRange)))
	 goto CLEANUP_1;

      if (!isInRange)  /* outside function range */
	 break; /* leave while loop */

#ifdef DEBUG
/* !!! test code */
      err = AdrGetAddrOffset(dasmAddrDesc, &tPCAddr);

/* !!! test code */
      err = AdrGetAddrOffset(linenumAddrDesc, &tStartAddr);
      err = AdrGetEndAddrOffset(linenumAddrDesc, &tEndAddr);
#endif

   }  /* end of while loop for finding call/returns */

   if (!localBkptCount) {
      err = ER_BX_NO_CALL_RETURN;
      /* fall thru to cleanup */
   }

CLEANUP_1:
   firstErr = err;
   err = AdrDestroyAddress(dasmAddrDesc);

CLEANUP_2:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(linenumAddrDesc);

CLEANUP_3:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(dummyAddrDesc);

CLEANUP_4:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(funcAddrDesc);

#ifdef DEBUG
   err = BxSendCliMessage((LPSTR)"end of local gountil");
   err = AdrCliDebug("", 1L, 0L);
#endif
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
} /* end of BxLocalGoUntilCallReturn */


/*---------------------------------------------------------------------------
** BxGoUntilCallReturnPostBkpt
**
** Design:
**    remove all temporary internal bkpts
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGoUntilCallReturnPostBkpt(VOID) {

   RETCODE err, firstErr;
   BOOLEAN tempMatch;

   firstErr = BxRemoveAllInternalBkpts();

   err = BxRemoveMatchingTemporary(&tempMatch);
   if (GOOD == firstErr)
      firstErr = err;

#ifdef DEBUG
   BxSendCliMessage((LPSTR)"completion of Go Until");
   BxHeapDump();
#endif

   err = EnlEventNotify(EVENT_BKPT_HALTED);
   if (GOOD == firstErr)
      firstErr = err;
   if (tempMatch)
      err = EnlEventNotify(EVENT_BKPT_EDIT);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
} /* end of BxGoUntilCallReturnPostBkpt */


/*---------------------------------------------------------------------------
** BxGoIntoCallReturn
**
** Design:
**    if the current instruction is a RET or (JSR and mode is call) then
**       step one instruction and stop
**    else
**       mark all returns and (calls if mode is call) then start execution
----------------------------------------------------------------------------*/
RETCODE EXPORT
BxGoIntoCallReturn(BX_GRANULARITY      gran,
		   BX_CALL_RETURN_TYPE callReturnType) {

   RETCODE     err, firstErr = GOOD;
   DESCRIPTOR  currentPCDesc, dummyAddrDesc;
   CALL_TYPE   instrType;
   U32 retPC;
   BOOLEAN inValid;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   if (callReturnType == BX_RETURN_ONLY) {
      GetReturnPC(&retPC, &inValid);
      if (inValid == TRUE) goto OTHER_WAY;
      err = iceRunUntil(retPC);
      Sds2AbiFwUnloadRegisters(TRUE);
      EnlEventNotify(EVENT_BKPT_HALTED);
      if (err == ICE_USER_HALT) return(ER_ABORT_FROM_ESC);
      else return (GOOD);
   }
OTHER_WAY:
   /* check to see if current instruction is call or return */
   if (GOOD != (err = CpuGetPC(&currentPCDesc)))   /* creates new addr */
      return err;
   if (GOOD != (err = DadSetDasmAddr(dasmID, currentPCDesc)))
      goto CLEANUP_2;

   /* disassemble instruction */
   if (GOOD != (err = AdrDuplicateAddress(currentPCDesc, &dummyAddrDesc)))
      goto CLEANUP_2;
   if (GOOD != (err = DadDasmIsCallOrRTS(dasmID, dummyAddrDesc, &instrType)))
      goto CLEANUP_1;

   if ((instrType == INST_RETURN) ||  /* see if call or return */
       ((instrType == INST_CALL) && (callReturnType == BX_CALL_RETURN))) {

      firstErr = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED);

      /* this function will single step target one instruction */
      err = BxGoIntoCallReturnPostBkpt(FALSE);  /* generates halted event */

   CLEANUP_1:
      if (GOOD == firstErr) firstErr = err;
      err = AdrDestroyAddress(dummyAddrDesc);

   CLEANUP_2:
      if (GOOD == firstErr) firstErr = err;
      err = AdrDestroyAddress(currentPCDesc);

      if (GOOD == firstErr) firstErr = err;
      return firstErr;
   }  /* end of if */

   /*-----------------------------------------------------------*/
   /* fall thru if not call/return instruction and set up bkpts */
   /*-----------------------------------------------------------*/

   /* use asm granularity in order to set bkpts on JSR and RTS so after
      the bkpt, one single step will go into or out of the function */
   if (GOOD != (err = BxLocalGoUntilCallReturn(BX_ASM_GRAN, callReturnType))) {
      BxRemoveAllInternalBkpts();
      return err;
   }

   /* set up for the callback */
   granState = gran;  /* needed after emu halted */

if(demonstrationVersion) {
   if (GOOD != (err = BxStepUntilBkpt())) {
      BxRemoveAllInternalBkpts();
      return err;
   }
} else {
   /* start emulation */
   if (GOOD != (err = BxMLStartEmulation(BX_GO_INTO))) {
      BxRemoveAllInternalBkpts();
      return err;
   }
}

   firstErr = BxWaitForEmulationHalt();  /* spins on emuRunningSpinFlag */
   /* can get error when no return instruction or in infinite loop
      and the user pushed ESC to get control back */
   err = BxGoIntoCallReturnPostBkpt(TRUE);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxGoIntoCallReturn */


/*---------------------------------------------------------------------------
** BxGoIntoCallReturnPostBkpt
** Design:
**    Remove any temporary bkpts that match current PC
**    if any internal bkpts were set that match current PC, remove
**    if currentPC == one of temporary internal bkpt or no bkpt was set
**       step one instruction
**       if GoInto granularity was asm, done
**       else
**          get range of line/stmt of currentPC
**          if addr is beginning addr of line/stmt, done
**          else step into by source amount (for looked-up range)
**    remove all temporary internal bkpts
**    propagate halted event
**    propagate bkpt changed event if a temporary was removed earlier
----------------------------------------------------------------------------*/
RETCODE PRIVATE
BxGoIntoCallReturnPostBkpt(BOOLEAN bkptWasSet) {

   RETCODE      err, firstErr = GOOD;
   DESCRIPTOR   currentPCDesc, linenumAddrDesc;
   BOOLEAN      match, tempMatch;
   ADDR_COMPARE compareResult;

   /* remove any temp bkpt that may match current pc */
   if (GOOD != (err = BxRemoveMatchingTemporary(&tempMatch))) {
      return err;
   }
   if (bkptWasSet) { /* only check for internal bkpt if some set */
      if (GOOD != (err = CpuGetPC(&currentPCDesc))) /* allocs address */
	 goto CLEANUP_2;

      if (GOOD != (err = BxBkptMatchInternal(currentPCDesc, &match)))
	 goto CLEANUP_1;

      if (GOOD != (err = AdrDestroyAddress(currentPCDesc)))
	 goto CLEANUP_2;
   }
   if ((match) || (!bkptWasSet)) {
      if (GOOD != (err = BxLLAsmStepInto(1L)))
	 goto CLEANUP_2;

      if (BX_ASM_GRAN != granState) {
	 /* get currentPC; get line/stmt range of that addr */
	 if (GOOD != (err = CpuGetPC(&currentPCDesc)))
	    goto CLEANUP_2;
	 if (GOOD != (err =
	    BxGetSrcAddressRange(currentPCDesc, granState, &linenumAddrDesc)))
	    goto CLEANUP_1;

	 /* compare currentPC addr to line/stmt start of range.
	    if same, done, else step into for this line/stmt */
	 if (GOOD != (err = AdrCompareAddresses(currentPCDesc,
						linenumAddrDesc,
						&compareResult)))
	    goto CLEANUP_1;
	 if (GOOD != (err = AdrDestroyAddress(currentPCDesc)))
	    goto CLEANUP_2;
	 if (GOOD != (err = AdrDestroyAddress(linenumAddrDesc)))
	    goto CLEANUP_2;

	 if (EQUAL_ADDRS != compareResult) {
	    err = BxSrcStep(BX_STEP_INTO, granState, 1L);
	 }
	 /* fall thru to CLEANUP_1 */
      }
   }
#ifdef DEBUG
   BxSendCliMessage((LPSTR)"completion of Go Into");
   BxHeapDump();
#endif

   if (GOOD != (err = BxRemoveAllInternalBkpts()))
      return err;  /* fall thru */
   err = EnlEventNotify(EVENT_BKPT_HALTED);
   if (GOOD == firstErr)
      firstErr = err;
   if (tempMatch)
      err = EnlEventNotify(EVENT_BKPT_EDIT);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;

CLEANUP_1:
   firstErr = err;
   err = AdrDestroyAddress(currentPCDesc);

CLEANUP_2:
   if (GOOD == firstErr) firstErr = err;
   err = BxRemoveAllInternalBkpts();  /* fall thru */
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
} /* end of BxGoIntoCallReturnPostBkpt */

VOID GetReturnPC(U32 *retPC, BOOLEAN *inValid)
{
RETCODE err;
MEM_ADDR_CLASS memClass;
SYM_TYPE_TYPE symType;
U32 offset;
U32 retType;
SYM_DESCRIPTOR symDesc,funcDesc,modDesc;
U32 spPtr, retIP, retCS, curSS;
U8 tmpBuff[10];
BOOLEAN framedFunction = TRUE;
DESCRIPTOR PCAddrDesc=NULL, tmpAddrDesc=NULL;
U8 level;
U32 attribute;
TYPE_HEADER_TYPE temphdr;
U8 tempbuf[MAX_SYMNAME_LENGTH];
FUNC_INFO_TYPE funcInfo;
ADDR_SEGSEL_TYPE segType;
U16 seg;
U32 addrSeg, funcStart;

   *inValid = TRUE;
   Sds2AbiFwUnloadRegisters(TRUE);
   if ((err = CpuGetPC(&PCAddrDesc)) != GOOD)
      goto CLEANUP2;
   if ((err = SymMapAddr2Symbol(PCAddrDesc,&memClass,&symType,&offset,
				&symDesc,&funcDesc,&modDesc)) != GOOD) {
      // Returns an err based on address discrepancies
      // not lack of symbol information. 
      goto CLEANUP2;
   }
   if ((funcDesc == NULL_SYMBOL) && (memClass != CODE_ADDR))  {
      goto CLEANUP2;
   }
   temphdr.typeName = (LPSTR)tempbuf;
   funcInfo.funcNamePtr = (LPSTR)funcInfo.funcName;
   *(funcInfo.funcNamePtr) = '\0';
   funcInfo.functionDesc = funcDesc;
   funcInfo.moduleDesc = modDesc;
   funcInfo.frameType = -1;
   funcInfo.numParams = 0;
   funcInfo.firstLocal = 0;
   funcInfo.localListCount = 0;
   if ((err = AdrCreateAddress(&funcInfo.funcAddrRangeDesc)) != GOOD)
      goto CLEANUP2;

   err = SymGetFunc(funcDesc,funcInfo.funcNamePtr,
      &funcInfo.funcClass,&funcInfo.stackSize,
      funcInfo.funcAddrRangeDesc);
   if (!err) {
      err = SymGetFuncType(funcDesc,&attribute,&funcInfo.frameType,
			     &funcInfo.pushMask, &temphdr,
			     &funcInfo.numParams, &level,
			     (LPSTR)tempbuf);
   }
   AdrDestroyAddress(funcInfo.funcAddrRangeDesc);
   if (!err) {
      retType = attribute;
   } else {
      funcInfo.funcAddrRangeDesc = NULL;
      goto CLEANUP2;
   }
 /* check the first byte of function is 0x55 (PUSH BP) or 0xc8 (ENTER) */
 /* if one of above code, it's a framed-function, otherwise it's frame-less */
   AdrCreateAddress(&tmpAddrDesc); 
   SymGetSymbolAddress(funcDesc, tmpAddrDesc);
   AdrGetAddrOffset(tmpAddrDesc, &funcStart);
   AdrGetAddrSegmentSelector(tmpAddrDesc, &segType, &seg);
   addrSeg = (U32)seg;
   funcStart += addrSeg << 16;
   iceGetMemN(funcStart, tmpBuff, 2);
   if ( tmpBuff[0] == 0xc8 || tmpBuff[0] == 0x55 || 
       (tmpBuff[0] == 0x66 && tmpBuff[1] == 0x55) ) { 
   } else 
      framedFunction = FALSE;
 /* offset < 3, it's means BP is not push completed */
   if (framedFunction == FALSE || offset < 3) { 
      SdnReadMember(SDN_SP, (U8*)&spPtr);
   } else {
      SdnReadMember(SDN_EBP, (U8*)&spPtr);
      spPtr += 2;
   }

   SdnReadMember(SDN_SS, (U8*)&curSS);
   spPtr += curSS << 16; 
   iceGetMemN(spPtr, tmpBuff, 2);
   retIP = *(U16 *)tmpBuff;
   if (retType) {
      spPtr += 2;
      iceGetMemN(spPtr, tmpBuff, 2);
      retCS = *(U16 *)tmpBuff;
   } else 
      SdnReadMember(SDN_CS, (U8*)&retCS);
   *retPC = (retCS << 16) + retIP;
   *inValid = FALSE;
CLEANUP2:
   if( tmpAddrDesc != NULL ) AdrDestroyAddress(tmpAddrDesc);
   AdrDestroyAddress(PCAddrDesc);
}


/*---------------------------------------------------------------------------
** BxStepUntilBkpt
**
** Purpose: internal debug routine to simulate execution until a breakpoint.
**          Disassembles instruction, checks for address in internal bkpt
**          list.
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxStepUntilBkpt(VOID) {

   RETCODE    err = GOOD, firstErr;
   BOOLEAN    match;
   DESCRIPTOR currentPCDesc, nextPCDesc;
   S8         addrBuf[ADDR_BUFF_SZ];
   LPSTR      buf;
   U16        bufLength;
   S8         tmpBuf[80];
   BOOLEAN    abortFromEsc;

   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD) return err;

   while (TRUE) {
      if (GOOD != (err = CpuGetPC(&currentPCDesc))) /* alloc's addr */
	 return err;
      if (GOOD != (err = DadSetDasmAddr(dasmID, currentPCDesc)))
	 return err;
      firstErr = DadDasmInst(dasmID, 1, &buf, &bufLength);

      err = BxSendCliMessage(buf);
      if (GOOD == firstErr)
	 firstErr = err;

      err = AdrDestroyAddress(currentPCDesc);
      if (GOOD == firstErr)
	 firstErr = err;

      err = TFree((LPSTR)(buf));
      if (GOOD == firstErr)
	 firstErr = err;

      if (GOOD != firstErr)
	 return firstErr;

      /* get next address; alloc's addr */
      if (GOOD != (err = DadGetDasmAddr(dasmID, &currentPCDesc))) /* new adr*/
	 return err;
      if (GOOD != (err = AdrDuplicateAddress(currentPCDesc, &nextPCDesc)))
	 goto CLEANUP_1;  /* nextPCDesc valid */
      /* set current PC to that value; TFrees descriptor */
      if (GOOD != (err = CpuSetPC(currentPCDesc)))  /* use up currentPCDesc */
	 return err;

      /* only nextPCDesc valid here */
      /* check for bkpt */
      if (GOOD != (err = BxBkptMatchInternal(nextPCDesc, &match)))
	 goto CLEANUP_1;
      if (match) {
	 if (GOOD !=
	    (err = AdrConvAddressToTextWithParams(nextPCDesc,
						  TRUE,
						  FALSE,
						  addrBuf)))
	    goto CLEANUP_1;
	 tmpBuf[0] = '\0';
	 strcat(tmpBuf, "internal breakpoint reached at address ");
	 strcat(tmpBuf, addrBuf);
	 if (GOOD != (err = BxSendCliMessage(tmpBuf)))
	    goto CLEANUP_1;
      }
      if (GOOD != (err = AdrDestroyAddress(nextPCDesc)))
	 goto CLEANUP_2;

      TskCheckAbort(&abortFromEsc);  // ignore any error
      if (abortFromEsc)
	 goto CLEANUP_2;
   } /* end of while */

CLEANUP_1:
   firstErr = err;
   err = AdrDestroyAddress(nextPCDesc);

CLEANUP_2:
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
} /* end of BxStepUntilBkpt */

/*---------------------------------------------------------------------------
** BxAsmStepIntoOrOver
**
** Purpose: exported, callable assembly step
----------------------------------------------------------------------------*/
RETCODE EXPORT BxAsmStepIntoOrOver(U32 stepCount, BX_STEP_TYPE stepType) {

   RETCODE err, firstErr;

   if (stepCount == 0L)  /* do nothing if user entered 0 */
      return GOOD;

   if ((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   firstErr = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED);

   if (BX_STEP_OVER == stepType) {
      err = BxLLAsmStepOver(stepCount);
   } else {
      err = BxLLAsmStepInto(stepCount);
   }

   if (GOOD == firstErr)
      firstErr = err;
   if (firstErr != GOOD) return(firstErr);   /* No event if step failed */

   err = EnlEventNotify(EVENT_BKPT_HALTED);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxAsmStepIntoOrOver */


/*---------------------------------------------------------------------------
** BxAsmStep   !!! delete when above function used in Actor !!!!!!!!!!!
**
** Purpose: exported, callable assembly step
----------------------------------------------------------------------------*/
RETCODE EXPORT BxAsmStep(U32 stepCount) {

   RETCODE err, firstErr;

   if (!stepCount)  /* do nothing if user entered 0 */
      return GOOD;

   if ((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   firstErr = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED);

   err = BxLLAsmStepInto(stepCount);
   if (GOOD == firstErr)
      firstErr = err;
   if (firstErr != GOOD) return(firstErr);   /* No event if step failed */

   err = EnlEventNotify(EVENT_BKPT_HALTED);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxAsmStep */

RETCODE EXPORT BxAsmStepRange(U32 addr1, U32 addr2) {

   RETCODE err, firstErr;
   U16 stepCount = 1;
   DESCRIPTOR currentPCDesc;
   U32 currentPC;
   BOOLEAN            abortFromEsc;

   if ((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   err = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED);
   if (err != GOOD) return(err);

   for (;;) {
      firstErr = TskCheckAbort(&abortFromEsc);
      if (firstErr!=GOOD) break;
      if (abortFromEsc) {
    	 firstErr = ER_ABORT_FROM_ESC;
	     break;
      }
      firstErr = BxLLAsmStepInto(stepCount);
      if (firstErr != GOOD) break;
      if (GOOD != (err = CpuGetPC(&currentPCDesc))) break;
      AdrGetAddrPhysicalOffset(currentPCDesc, &currentPC);
      if (currentPC < addr1 || currentPC > addr2) break;
   }
   err = EnlEventNotify(EVENT_BKPT_HALTED);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxAsmStepRange */


/*---------------------------------------------------------------------------
**  BxLLAsmStepInto
----------------------------------------------------------------------------*/
RETCODE BxLLAsmStepInto(U32 stepCount) {

   RETCODE            err = GOOD, firstErr;
   BOOLEAN            timedOut;
   U32                localStepCount;
   BOOLEAN            lastStep;  /* indicates when to exit step loop */
   BOOLEAN            abortFromEsc;


#define STEP_INCREMENT 40L

   DESCRIPTOR currentPCDesc;
   LPSTR      buf;
   U16        bufLength;
   U8         enable = FALSE;  /* must be this size for SD to work */

if(!demonstrationVersion) {
   /* set up stepping flag for duration of step */
   steppingFlag = TRUE;
   do {
      if (stepCount > STEP_INCREMENT) {
	 stepCount = stepCount - STEP_INCREMENT;
	 localStepCount = STEP_INCREMENT;
	 lastStep = FALSE;
      } else {
	 /* last step */
	 localStepCount = stepCount;
	 lastStep = TRUE;
      }
      /* only returns when emulator has started stepping */
      /* disable all bkpts */
      if((err = SdWriteMember(bkEnableDesc, (U8 FAR *)&enable, GOOD))!=GOOD)
	 goto CLEANUP;
      if((err = SdWriteCmdReadResponse(singleStepDesc,
				       (U8 FAR *)&localStepCount,
				       GOOD,
				       emulResultDesc,
				       0L,
				       &timedOut)
	 )!=GOOD)
	 goto CLEANUP;
      /* check for exit in the outer step loop */
      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) {
	 goto CLEANUP;
      }
      if(abortFromEsc) {
	 err = ER_ABORT_FROM_ESC;
	 goto CLEANUP;
      }
   } while (!lastStep);  /* end of outer while */

} else {

   localStepCount = 1L;
   do {
      if (stepCount > 1L) {
	 stepCount--;
	 lastStep = FALSE;
      } else {
	 /* last step */
	 lastStep = TRUE;
      }

      /* display instruction to be executed and increment PC */
      if (GOOD != (err = CpuGetPC(&currentPCDesc)))
	 goto CLEANUP;
      if (GOOD != (err = DadSetDasmAddr(dasmID, currentPCDesc)))
	 goto CLEANUP;
      if (GOOD != (err = DadDasmInst(dasmID, 1, &buf, &bufLength)))
	 goto CLEANUP;
      if (GOOD != (err = BxSendCliMessage(buf)))
	 goto CLEANUP;

      /* could, in future, create stack of return addresses upon seeing call.
	 Upon return, pops stack and returns that address (simulating uP) */
      /* get next address */
      if (GOOD != (err = DadGetDasmAddr(dasmID, &currentPCDesc)))
	 goto CLEANUP;
      /* set current PC to that value; TFrees descriptor */
      if (GOOD != (err = CpuSetPC(currentPCDesc)))
	 goto CLEANUP;

      /* only returns when emulator has started stepping */
      if((err = SdWriteCmdReadResponse(singleStepDesc,
				       (U8 FAR *)&localStepCount,
				       GOOD,
				       emulResultDesc,
				       0L,
				       &timedOut)
	 )!=GOOD)
	 goto CLEANUP;

      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) {
	 goto CLEANUP;
      }
      if (abortFromEsc) {
	 err = ER_ABORT_FROM_ESC;
	 goto CLEANUP;
      }
   } while (!lastStep);  /* end of outer while */

}

CLEANUP:
   steppingFlag = FALSE;
   firstErr = err;
   /* update system cause variable when stepping done */
   {
      BREAK_CAUSE     bkptCause;

      err = SdnReadMember(SDN_BREAK_CAUSE, (U8 FAR *)&bkptCause);
      if (GOOD == err) {
	 if (bkptCause != lastBkptCause) {
	    /* update system var */
	    bkptCauseSystemVar.integerValue = bkptCause;

	    SendMessage(cliServerHandle,  /* send system var info to CLI */
			CLI_SYSTEM_VARIABLE,
			0,                   /* don't dealloc mem */
			(DWORD)&bkptCauseSystemVar);
	    lastBkptCause = bkptCause;
	 }
      }
   }
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
}  /* end of BxLLAsmStepInto */


/*---------------------------------------------------------------------------
** BxLLAsmStepOver
**
** Purpose: implements step over mode; uses bkpt when JSR encountered
----------------------------------------------------------------------------*/
RETCODE BxLLAsmStepOver(U32 stepCount) {

   RETCODE            err = GOOD, firstErr = GOOD;
   U32                loopCount;
   DESCRIPTOR         currentPCDesc, nextAddrDesc;
   DESCRIPTOR         bkptDesc;  /* holds return from setting bkpt */
   DESCRIPTOR         bkptAddrDesc;
   CALL_TYPE          instrType;
   ADDR_COMPARE       compareResult;
   BOOLEAN            abortFromEsc;


#ifdef DEBUG
/* !!! test vars */
   U32 tStartAddr, tEndAddr;
#endif

   steppingFlag = TRUE;
/* clear any ESC previously hit */
   err = TskCheckAbort(&abortFromEsc);
   if(err!=GOOD) return err;

   /* use the current PC for breakpoint */
   if (GOOD != (err = CpuGetPC(&bkptAddrDesc)))
      return err;

   /* outer loop for step <count> times */
   for (loopCount = stepCount; loopCount > 0; loopCount--)  {
      /* CpuGetPC alloc's address */
      if (GOOD != (err = CpuGetPC(&currentPCDesc)))
	 goto CLEANUP_2;

      /* set up start address of disassembly; does NOT consume addr */
      if (GOOD != (err = DadSetDasmAddr(dasmID, currentPCDesc)))
	 goto CLEANUP_1;

#ifdef DEBUG
/* !!! test code */
      err = AdrGetAddrOffset(currentPCDesc, &tStartAddr);
      err = AdrGetEndAddrOffset(currentPCDesc, &tEndAddr);
#endif

      /* disassemble instruction */
      if (GOOD != (err =
	 DadDasmIsCallOrRTS(dasmID, currentPCDesc, &instrType)))
	 goto CLEANUP_1;

      if (instrType != INST_CALL) {
	 err = BxLLAsmStepInto(1L);   /* step one instruction */
	 if (GOOD != err) {
	    goto CLEANUP_1;
	 }
      } else {
	 /* set breakpoint on instruction following */
	 /* creates new addr */
	 if (GOOD != (err = DadGetDasmAddr(dasmID, &nextAddrDesc)))
	    goto CLEANUP_1;
	 /* remove processor dependency */
	 if (FAMILY_68K == staticProcFamily) {
	    if(GOOD != (err = AdrSetAddrSpace(nextAddrDesc, SPACE_SP)))
	       return err;
	 }
	 if (GOOD != (err = AdrCopyAddress(nextAddrDesc,
					   bkptAddrDesc)))
	    goto CLEANUP_1;

#ifdef DEBUG
/* !!! test code */
      err = AdrGetAddrOffset(bkptAddrDesc, &tStartAddr);
      err = AdrGetEndAddrOffset(bkptAddrDesc, &tEndAddr);
#endif

	 /* set breakpoint after CALL instruction */
	 /* consumes address */
	 if (GOOD != (err =
	    BxSetBkpt(BX_ENABLED, BX_TEMPORARY_INTERNAL,
		      nextAddrDesc, &bkptDesc)))
	    goto CLEANUP_1;

if(!demonstrationVersion) {
	 /* start emulation */
	 steppingFlag = FALSE; /* disable so events will be generated */
	 if (GOOD != (err = BxMLStartEmulation(BX_GO_STEP))) {
	    BxRemoveAllInternalBkpts();
	    goto CLEANUP_1;
	 }

	 /* spins on emuRunningSpinFlag */
	 if (GOOD != (err = BxWaitForEmulationHalt())) {
	    /* NOTE: purposely ignoring error since need to report
	       error from previous function call */
	    /* can easily get here if JSR is in infinite loop and user
	       must hit ESC to get control back */
	    /* get rid of any internal bkpts set by this function */
	    BxRemoveAllInternalBkpts();
	    goto CLEANUP_1;
	 }
	 steppingFlag = TRUE;
	 /* remove temporary step bkpt after CALL instruction */
	 if (GOOD != (err = BxRemoveBkpt(bkptDesc)))
	    goto CLEANUP_1;

	 /* test to see if target stopped on step bkpt */
	 /* delete previous PC addr so can get new one */
	 if (GOOD != (err = AdrDestroyAddress(currentPCDesc)))
	    goto CLEANUP_2;

	 /* creates new addr */
	 if (GOOD != (err = CpuGetPC(&currentPCDesc)))
	    goto CLEANUP_2;

	 if (GOOD != (err =
	    AdrCompareAddresses(currentPCDesc,
				bkptAddrDesc,
				&compareResult)))
	    goto CLEANUP_2;
	 if (EQUAL_ADDRS != compareResult) {
	    err = GOOD;    /* stop stepping because not step bkpt */
	    goto CLEANUP_1;
	 }
} else {
	 if (GOOD != (err = BxStepUntilBkpt()))
	    return err;
}
      }  /* end of if (instrType == INST_CALL) */
   }  /* end of for loop */

CLEANUP_1:

   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(currentPCDesc);

CLEANUP_2:
   if (GOOD == firstErr) firstErr = err;
   err = AdrDestroyAddress(bkptAddrDesc);
   if (GOOD == firstErr) firstErr = err;

   steppingFlag = FALSE;
   return firstErr;
}  /* end of BxLLAsmStepOver */


/*---------------------------------------------------------------------------
** BxStartEmulation
----------------------------------------------------------------------------*/
RETCODE EXPORT BxStartEmulation(VOID) {

   return  BxMLStartEmulation(BX_GO_FOREVER);
}  /* end of BxStartEmulation */


/*---------------------------------------------------------------------------
** BxMLStartEmulation - medium level
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxMLStartEmulation(BX_GO_TYPE inputState) {

   goState = inputState;
   return BxLLStartEmulation();
}  /* end of BxMLStartEmulation */


/*---------------------------------------------------------------------------
** BxLLStartEmulation - Low level start emulation
**
** Purpose: separate goState from callers
----------------------------------------------------------------------------*/
RETCODE PRIVATE BxLLStartEmulation(VOID) {

   RETCODE err;
   U8      goCmd = TRUE;
   U8      enable = TRUE;  /* U8 size required for SD to work */
   BOOLEAN timedOut;
   HCURSOR hOldCursor, hHourGlassCursor;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   /* with a tasking model, this code can possibly be entered between the time
      the user clicks GO and the emulator starts.  The following 'if' will
      not start the emu again if the spin flag is already set
   */
   if (emuRunningSpinFlag) {
      return GOOD;
   } else {
      /* change state of running spin flag before starting emulation */
      emuRunningSpinFlag = TRUE;
   }
   /* put up hour glass */
   hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
   hOldCursor = SetCursor(hHourGlassCursor);

   if((goState == BX_GO_FOREVER) && (staticSystemType==PROC_POWERPACK)) {
      /* start the trace before emulation */
      if((err = TrcTracingSet(TRUE))!=GOOD) {
	 emuRunningSpinFlag = FALSE;
	 return err;
      }
   }

   /* enable breakpoints */
   if ((err = SdWriteMember(bkEnableDesc, (U8 FAR *)&enable, GOOD))!=GOOD) {
      emuRunningSpinFlag = FALSE;
      return err; /* don't start emulation if err */
   }
   err = SdWriteCmdReadResponse(goDesc,
				(U8 FAR *)&goCmd,
				GOOD,
				emulResultDesc,
				0L,
				&timedOut);
   SetCursor(hOldCursor);
   if (err != GOOD)
      emuRunningSpinFlag = FALSE;  /* go command failed */
   return err;
}  /* end of BxLLStartEmulation */


/*---------------------------------------------------------------------------
** BxResetAndStartEmulation - Reset target then start emulation
----------------------------------------------------------------------------*/
RETCODE EXPORT BxResetAndStartEmulation(VOID) {

   RETCODE         err;
   U8              resetAndGoCmd = TRUE;
   U8              enable = TRUE;  /* U8 size required for SD to work */
   BOOLEAN         timedOut;
   HCURSOR hOldCursor, hHourGlassCursor;

   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD)
      return err;

   if (emuRunningSpinFlag) {
      return GOOD;
   } else {
      /* change state of running spin flag before starting emulation */
      emuRunningSpinFlag = TRUE;
   }
   /* put up hour glass */
   hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
   hOldCursor = SetCursor(hHourGlassCursor);

   if (staticSystemType==PROC_POWERPACK) {
      /* start the trace before emulation */
      if ((err = TrcTracingSet(TRUE))!=GOOD) {
	 emuRunningSpinFlag = FALSE;
	 return err;
      }
   }

   /* enable breakpoints */
   if ((err = SdWriteMember(bkEnableDesc, (U8 FAR *)&enable, GOOD))!=GOOD) {
      emuRunningSpinFlag = FALSE;
      return err; /* don't start emulation if err */
   }
   err = SdWriteCmdReadResponse(resetAndGoDesc,
				(U8 FAR *)&resetAndGoCmd,
				GOOD,
				emulResultDesc,
				0L,
				&timedOut);
   SetCursor(hOldCursor);
   if (err != GOOD) emuRunningSpinFlag = FALSE;
   return err;
}  /* end of BxResetAndStartEmulation */


/*---------------------------------------------------------------------------
** BxWaitForEmulationHalt
**
** Design:
**    Waits for emuRunningSpinFlag to change state.  This static is
**    modified by a callback when the emulator has reached a breakpoint.
**
**    If user hists ESC, emulator is halted, then returns
----------------------------------------------------------------------------*/
#pragma warn -aus
RETCODE PRIVATE BxWaitForEmulationHalt(VOID) {

   EMULATION_STATE emulState;
   RETCODE         err;
   MSG             msg;
   BOOLEAN         abortFromEsc;

   while (TRUE) {
      /* process one message, then check for emulator halted */
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
	 if (msg.message == WM_QUIT)
	    break;
	 TranslateMessage(&msg);
	 DispatchMessage(&msg);
      }
      /* must read SD to see if state change; if so, callback will set
	 emuRunningSpinFlag FALSE */
      err = SdnReadMember(SDN_EMULATION_STATE, (U8 FAR*)&emulState);

    // test for ESC key
      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) {
	 BxLLHaltEmulation();       // ignore any error from halt
	 emuRunningSpinFlag = FALSE;
	 return err;
      }
      if (abortFromEsc) {
	 err = BxLLHaltEmulation();
	 emuRunningSpinFlag = FALSE;
	 return ER_ABORT_FROM_ESC;
      }

      if (!emuRunningSpinFlag)  /* flag cleared by callback */
	 break;
   }
   return GOOD;
}  /* end of BxWaitForEmulationHalt */


/*---------------------------------------------------------------------------
**  BxHaltEmulation
----------------------------------------------------------------------------*/
RETCODE EXPORT BxHaltEmulation(VOID) {

   return BxLLHaltEmulation();

} /* end of BxHaltEmulation */


/*---------------------------------------------------------------------------
**  BxLLHaltEmulation
----------------------------------------------------------------------------*/
RETCODE BxLLHaltEmulation(VOID) {

   RETCODE            err;
   U8                 haltCmd = TRUE;
   BOOLEAN            timedOut;
   U8 goEndFlag;

   iceGetCpuStatus(&goEndFlag);
   if (goEndFlag & 0x01) return GOOD;  /* <Judy 5/30/1997> */
 
   err = SdWriteCmdReadResponse(haltDesc,
				(U8 FAR *)&haltCmd,
				GOOD,
				emulResultDesc,
				0L,
				&timedOut);
   return err; 
}  /* end of BxLLHaltEmulation */


/*---------------------------------------------------------------------------
** BxEmulStateChange
----------------------------------------------------------------------------*/
VOID EXPORT BxEmulStateChange(DESCRIPTOR desc)  {

   EMULATION_STATE emulState;
   RETCODE         err, firstErr;
   BREAK_CAUSE     bkptCause = CAUSE_NONE;
   DESCRIPTOR      desc1;

   if (steppingFlag)  /* skip the remainder of this function if in
			 stepping mode */
      return;

   firstErr = SdReadMember(desc, (U8 FAR*)&emulState);
   if (GOOD != firstErr) {
      goto CLEANUP;
   }
   switch(emulState) {
      case EM_RUNNING:
      case EM_STEPPING:
	 /* update emulating system var, then send to CLI */
	 emulatingSystemVar.integerValue = BX_EMULATING;
	 SendMessage(cliServerHandle,
		     CLI_SYSTEM_VARIABLE,
		     0,                   /* don't dealloc mem */
		     (DWORD)&emulatingSystemVar);

	 if ((err = SdRegister("break cause",NULLPTR,&desc1)) != GOOD)
	    return(err);
	 firstErr = SdReadMemberNoInputCheck(desc1, (U8 FAR *)&bkptCause);
	 if (GOOD == firstErr) {
	    if (bkptCause == CAUSE_PENDING && bkptCause != lastBkptCause) {
	       firstErr = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_PENDING);
	       lastBkptCause = bkptCause;
	       break;
	    }
	 }
	 else break;
	 /* skipping the low level event notification - at this point, it
	    is not needed */
	 firstErr = EnlEventNotify(EVENT_HL_BKPTEXEC_EMUL_STARTED);
	 break;

      case EM_HALTED:
	 /* get the cause; if different than last time, update system var */
	 firstErr = SdnReadMember(SDN_BREAK_CAUSE, (U8 FAR *)&bkptCause);
	 if (GOOD == firstErr) {
	    if (bkptCause != lastBkptCause) {
	       /* update system var */
	       bkptCauseSystemVar.integerValue = bkptCause;
	       SendMessage(cliServerHandle,  /* send system var info to CLI */
			   CLI_SYSTEM_VARIABLE,
			   0,                   /* don't dealloc mem */
			   (DWORD)&bkptCauseSystemVar);
	       lastBkptCause = bkptCause;
	    }
	 }
	 /* fall thru */
      case EM_HUNG:
	 /* update emulating system var, then send to CLI */
	 emulatingSystemVar.integerValue = BX_NOT_EMULATING;
	 SendMessage(cliServerHandle,
		     CLI_SYSTEM_VARIABLE,
		     0,                   /* don't dealloc mem */
		     (DWORD)&emulatingSystemVar);
	 if (goState == BX_GO_FOREVER) {
	    BOOLEAN tempMatch;
	    /* remove go to cursor internal if programmed */
	    firstErr = BxRemoveAllInternalBkpts();
	    err = BxRemoveMatchingTemporary(&tempMatch);
	    if (GOOD == firstErr) firstErr = err;
	    err = EnlEventNotify(EVENT_BKPT_HALTED);
	    if (GOOD == firstErr) firstErr = err;

	    /* generate bkpt changed event if one matched the current pc */
	    if (tempMatch)
	       err = EnlEventNotify(EVENT_BKPT_EDIT);
	    if (GOOD == firstErr) firstErr = err;

	    if (GOOD != firstErr) {
	       if (ER_ABORT_FROM_ESC == firstErr) {
		  globalEscPushed = ER_ABORT_FROM_ESC;
	       } else {
		  ErrDisplayError(firstErr, CHECK_MODE);
	       }
	    }
	 } else {
	    /* 10/19/20 - Nghia
	    ** Reset while processor halted need to generate BKPT_HALTED
	    ** event.  
	    */
	    if ((emulState == EM_HALTED) &&
		((err = EnlEventNotify(EVENT_BKPT_HALTED)) != GOOD)) {
	       ErrDisplayError(err, CHECK_MODE);
	    }
	 }
	 emuRunningSpinFlag = FALSE;
	 break;

   } /* end of switch */


CLEANUP:
   if (GOOD != firstErr) {
      ErrDisplayError(firstErr, CHECK_MODE);
   }
   return;
}  /* end of BxEmulStateChange */

/***************************************************************************
**
** BxSetStepMask
** BxGetStepMask
**
*****************************************************************************/
#define INI_FILENAME "pwrviews.ini"
#define INI_STEP_MASK_SECTION "ToolBarInfo"
#define INI_STEP_MASK "stepMask"

RETCODE InitStepMask(VOID) {
   S8 buf[10];
   GetPrivateProfileString(INI_STEP_MASK_SECTION, INI_STEP_MASK, "0", buf,
      sizeof(buf), INI_FILENAME);
   if (stricmp(buf, "0") == 0) return(BxSetStepMask(FALSE));
   else return(BxSetStepMask(TRUE));
}

RETCODE EXPORT BxSetStepMask(BOOLEAN stepMask) {
   WritePrivateProfileString(INI_STEP_MASK_SECTION, INI_STEP_MASK,
      stepMask ? "1" : "0", INI_FILENAME);
   return(SdnWriteMember(SDN_STEP_MASK, (U8*)&stepMask, GOOD));
}

RETCODE EXPORT BxGetStepMask(BOOLEAN *stepMask) {
   return(SdnReadMember(SDN_STEP_MASK, (U8*)stepMask));
}


/******************************** E O F ***********************************/
