/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/Windbg.c 34    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  Main debugger support/Win32 interface
  
  Main debugger support for the Solace Sol-20 emulator
  Copyright (c) Jim Battle, 2000-2002

  Modifications for the Altair32 Emulator
  Copyright (c) 2001-2006 Richard A. Cini

  The debugger has a few different states:
	1) non-existant
	2) active   (when the CPU is stopped)
	3) inactive (showing, but only command and history windows are active)
	4) hidden   (not shown)

	We are notified by the core emulator what to do via UI_DbgWin


Change Log:
  2001/11/07  RAC -- Initial changes
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2002/01/02  RAC -- Synched with Solace 3.0
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/05/17  RAC -- Changes made to sync to Solace v3.1
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2004/01/20  RAC -- Diff'ed changes from FJS (Fred J. Scipione) 
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/

// some ideas for enhancement:
//  *) add trace [nn] and tracei [nn] commands.  like step/stepi,
//     except it logs instruction and register state into log window
//     before/after? each step/stepi.
//     or perhaps a "trace on/off" command that causes logging as
//     each instruction is executed in a subsequent step/stepi.
//  *) keep track of register/memory state at each breakpoint
//     and use this info to draw changed values in red
//  *) add ability to log output of window:
//        (NEW)LOG <filename>, LOG OFF, LOG ON
//  *) allow resizing of panes (at least between dasm and log windows)
//  *) allow reading in a symbol table;
//     use for expressions and disassembly
//  *) allow specifying a source .prn and use this for
//     disassembly when appropriate


#include <windows.h>
#include <windowsx.h>	// message cracker macros
#include <stdlib.h>
#include <stdio.h>		// C-lib - I/O 
#include <string.h>
#include "altair32.h"
//#include "extensions.h" // corrections for bad functions in windowsx
#include "windbg.h"
#include "windbg_sub.h"
#include "srcdbg.h"	// interface to source-level debugger


#define SPLITTER_HEIGHT 2	// size of splitter bar

// overall debugger state
dbgstate_t dbgstate;

static void update_debugwin(int update_flags);
static void activate_debugwin_step(void);
static void inactivate_debugwin(void);
static void activate_debugwin(void);
static void destroy_debugwin(void);
static void update_debugwin(int update_flags);
static void close_debugwin(void);

void DrawXorBar(HDC hdc, int pos);


// ===============================================================
// this routine is used by any subwindow to gray out the
// area if the window is disabled
// ===============================================================

void 
GrayOutWindow(HWND hwnd, HDC hdc)
{
    // color the display gray & disable scrollbars
    COLORREF oldcolor;
    HBRUSH newbrush, oldbrush;
    HPEN oldpen, newpen;
    RECT rc;

    COLORREF bg = RGB(0xD0, 0xD0, 0xD0);

    GetClientRect(hwnd, &rc);

    oldcolor = SetBkColor(hdc, bg);
    newbrush = CreateSolidBrush(bg);
    newpen   = CreatePen(PS_SOLID, 1, bg);
    oldbrush = SelectObject(hdc, newbrush);
    oldpen   = SelectObject(hdc, newpen);

    Rectangle(hdc, 0, 0, rc.right, rc.bottom);	// l, t, r, b

    SetBkColor(hdc, oldcolor);
    SelectObject(hdc, oldbrush);
    SelectObject(hdc, oldpen);
    DeleteObject(newbrush);
    DeleteObject(newpen);
}


void 
handle_fkey(UINT key)
{
    switch (key) {
	//case VK_F5: Sys_DbgNotify(RUN_KEY, 0); break;
	case VK_F5: UI_DbgWin(DW_Close, 0); break;
	case VK_F6: Sys_DbgNotify(STEP_KEY, 1); break;
	case VK_F7: Sys_DbgNotify(STEPI_KEY, 1); break;
	default: break;
    }
}


// ==================================================================
// the following are related to the main debugger window
// ==================================================================

HWND 
CreateDbgWindow(void)
{
    // typical windows window
    DWORD winstyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;

    HWND hwnd;

    // open up debugging container window
    hwnd = CreateWindow(
		"SolaceDbgWindow",	// window class name
		"Altair32 Debug",	// window caption
		winstyle,			// window style
		CW_USEDEFAULT,		// initial x position
		0,					// initial y position
		CW_USEDEFAULT,		// initial x size
		0,					// initial y size
		NULL,				// parent window handle
		NULL,				// window menu handle
		winstate.hInst,				// program instance handle
		NULL);				// creation parameters

    ASSERT(hwnd != NULL);

    return hwnd;
}


static BOOL 
OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
    HDC hdc = GetDC(hwnd);
    int saved = SaveDC(hdc);

    int PointSize = 8;
    int nHeight = -MulDiv(PointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);

    dbgstate.cour_font = CreateFont(
			nHeight,	// height
			0,		// width (default)
			0,0,		// escapement, orientation
			FW_REGULAR,	// weight
			0,0,0,		// italic,underline,strikeout
			ANSI_CHARSET,	// maybe DEFAULT_CHARSET
			OUT_RASTER_PRECIS,
			CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY,
			FIXED_PITCH | FF_MODERN,
			"Courier New"
			);

    dbgstate.fixed_font = (dbgstate.cour_font == NULL) ? GetStockFont(ANSI_FIXED_FONT)
						       : dbgstate.cour_font;

    // get some basic information about the font we are using
    {
	TEXTMETRIC fix_tm;		// info about the fixed font

	if (dbgstate.fixed_font != NULL)
	    SelectFont(hdc, dbgstate.fixed_font);

	GetTextMetrics(hdc, &fix_tm);

	dbgstate.fix_x = fix_tm.tmAveCharWidth;
	dbgstate.fix_y = fix_tm.tmHeight + fix_tm.tmExternalLeading;
    }


    // if it was opened & closed previously, use the size/location
    // from when it was last closed.
   if (dbgstate.valid_position) {
	MoveWindow(hwnd, dbgstate.position.left, dbgstate.position.top,
		   (dbgstate.position.right  - dbgstate.position.left),
		   (dbgstate.position.bottom - dbgstate.position.top),
		   TRUE);
    } else {
	// guess a "good" initial size; leave position, but change size.
	// reg window is 16 chars wide, plus two border pixels;
	// the cmd window should be about 72 wide, plus three borders
	// of four pixels apiece, plus some pad.
	int width  = (16+72+2)*dbgstate.fix_x + 3*4 + 10;
	int height = 480;
	SetWindowPos(hwnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE);
    }

    RestoreDC(hdc, saved);
    ReleaseDC(hwnd, hdc);

    // create subordinate windows
    dbgstate.hRegWnd  = CreateRegWindow(hwnd);
    dbgstate.hMemWnd  = CreateMemWindow(hwnd);
    dbgstate.hDasmWnd = CreateDasmWindow(hwnd);
    dbgstate.hCmdWnd  = CreateCmdWindow(hwnd);
    dbgstate.hLogWnd  = CreateLogWindow(hwnd);

    dbgstate.hSplitCursor = LoadCursor(NULL, IDC_SIZENS);
    dbgstate.splitflag    = FALSE;
    dbgstate.splitcursor  = FALSE;
    dbgstate.splitpercent = 50;

    update_debugwin(UPDATE_ALL);

    //WinUpdateToolBar();

    return TRUE;
}


// handle window resize events
// we mainly just compute what the sizes of the subordinate
// windows should be and send them messages to resize.
//
// the layout is as follows:
//   +-----+-------------------------------------+
//   |     |                                     |
//   |     |  dasm window                        |
//   |regs |                                     |
//   |     |-------------------------------------+
//   |     |                                     |
//   |-----|  command output (hLogWnd)           |
//   |     |                                     |
//   | mem |                                     |
//   |     |-------------------------------------|
//   |     |  command prompt (hCmdWnd)           |
//   +-----+-------------------------------------+
//
// the size of regs and command prompt are fixed;
// dasm and command output windows split what is left,
// and stack is as tall as it can be.

static void 
RecomputeWindows(void)
{
    int splitborder = SPLITTER_HEIGHT;
    int border = 2;
    int cx = dbgstate.xClient;
    int cy = dbgstate.yClient;

    int reg_top, reg_left, reg_width, reg_height;
    int mem_top, mem_left, mem_width, mem_height;
    int cmd_top, cmd_left, cmd_width, cmd_height;
    int dasm_left, dasm_width, dasm_height, dasm_top;
    int log_left, log_width, log_height, log_top;

    // the two left windows are of fixed width:
    // width is 16 characters wide:
    //   DE=hhhh=ddddd
    //   STACK nnnn:hhhh

    reg_top    = border;
    reg_left   = 0;
    reg_width  = dbgstate.fix_x * 16;	// 16 chars wide
    reg_height = dbgstate.fix_y *  8;	//  8 chars tall

    mem_top    = reg_top + reg_height + border;
    mem_left   = 0;
    mem_width  = reg_width;
    mem_height = cy - mem_top;

    cmd_left   = reg_left + reg_width + border;
    cmd_width  = cx - cmd_left;
    cmd_height = (dbgstate.fix_y * 3)/2;
    cmd_top    = cy - cmd_height;

    dasm_top    = border;
    dasm_left   = cmd_left;
    dasm_width  = cmd_width;
    dasm_height = (dbgstate.splitpercent*(cmd_top - dasm_top - border - splitborder))
		/ 100;

    log_left   = dasm_left;
    log_top    = dasm_top + dasm_height + splitborder;
    log_width  = dasm_width;
    log_height = cmd_top - border - (dasm_top + dasm_height + splitborder);

    dbgstate.splitbar.left   = dasm_left + border;
    dbgstate.splitbar.right  = dbgstate.splitbar.left + dasm_width;
    dbgstate.splitbar.top    = dasm_top + dasm_height;
    dbgstate.splitbar.bottom = dbgstate.splitbar.top  + splitborder;

    dbgstate.splitrange.left   = dbgstate.splitbar.left;
    dbgstate.splitrange.right  = dbgstate.splitbar.right;
    dbgstate.splitrange.top    = dasm_top;
    dbgstate.splitrange.bottom = log_top + log_height;
   
    MoveWindow(dbgstate.hRegWnd,  reg_left,  reg_top,  reg_width,  reg_height,  TRUE);
    MoveWindow(dbgstate.hMemWnd,  mem_left,  mem_top,  mem_width,  mem_height,  TRUE);
    MoveWindow(dbgstate.hDasmWnd, dasm_left, dasm_top, dasm_width, dasm_height, TRUE);
    MoveWindow(dbgstate.hCmdWnd,  cmd_left,  cmd_top,  cmd_width,  cmd_height,  TRUE);
    MoveWindow(dbgstate.hLogWnd,  log_left,  log_top,  log_width,  log_height,  TRUE);

    update_debugwin(UPDATE_ALL);
}


static void
OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    dbgstate.xClient = cx;
    dbgstate.yClient = cy;

    RecomputeWindows();

    dbgstate.valid_position = TRUE;
}


static void 
OnPaint(HWND hwnd)
{
    PAINTSTRUCT	ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    int saved = SaveDC(hdc);
    
    RestoreDC(hdc, saved);
    EndPaint(hwnd, &ps);
}


// clamp splitter bar range to log window and dasm window
static void
ClampSplitter(POINT *pt)
{
    if (pt->y < dbgstate.splitrange.top)
	pt->y = dbgstate.splitrange.top;
    if (pt->y > dbgstate.splitrange.bottom-SPLITTER_HEIGHT)
	pt->y = dbgstate.splitrange.bottom-SPLITTER_HEIGHT;
}


// make coordinates relative to window, not client area
static void
RemapSplitRect(HWND hWnd, POINT *pt)
{
    RECT rect;
    ClientToScreen(hWnd, pt);
    GetWindowRect(hWnd, &rect);
    pt->x -= rect.left;
    pt->y -= rect.top;
}


static void
OnLButtonDown(HWND hWnd, BOOL dblclk, int xClient, int yClient, UINT keyflags)
{
//	POINT pt = { xClient, yClient };	// DELETED FJS
	POINT pt; // ADDED FJS
    pt.x = xClient; pt.y = yClient; // ADDED FJS

    if (PtInRect(&dbgstate.splitbar, pt)) {
	HDC hdc = GetWindowDC(hWnd);
	dbgstate.splitflag = TRUE;
	ClampSplitter(&pt);
	dbgstate.splitpos = pt.y;
	RemapSplitRect(hWnd, &pt); // convert client coords to screen coordinates
	SetCapture(hWnd);	// capture the mouse so we receive all messages
	DrawXorBar(hdc, pt.y);
	ReleaseDC(hWnd, hdc);
    }
}


static void
OnMouseMove(HWND hWnd, int xClient, int yClient, UINT keyflags)
{

    POINT pt; // ADDED FJS
    
	if (!dbgstate.splitflag) {

//	POINT pt = { xClient, yClient };	// DELETED FJS
	pt.x = xClient;	pt.y = yClient; // ADDED FJS

	if (PtInRect(&dbgstate.splitbar, pt)) {
	    SetCursor(dbgstate.hSplitCursor);
	    dbgstate.splitcursor = TRUE;
	} else if (dbgstate.splitcursor) {
	    SetCursor(winstate.hArrowCursor);
	    dbgstate.splitcursor = FALSE;
	}

    } else if (dbgstate.splitflag &&
	   (yClient != dbgstate.splitpos)) {

	HDC hdc = GetWindowDC(hWnd);
//	HCURSOR hCursor = LoadCursor(NULL, IDC_SIZENS);// DELETED FJS
    (void) LoadCursor(NULL, IDC_SIZENS); // ADDED FJS
//	POINT pt; // ADDED FJS
    pt.x = xClient; pt.y = dbgstate.splitpos; // ADDED FJS
	
	ClampSplitter(&pt);
	RemapSplitRect(hWnd, &pt);	// convert client coords to screen coordinates
	DrawXorBar(hdc, pt.y);		// Erase where the bar WAS

	pt.y = yClient;
	ClampSplitter(&pt);
	dbgstate.splitpos = yClient;

	RemapSplitRect(hWnd, &pt);	// convert client coords to screen coordinates
	DrawXorBar(hdc, pt.y);		// Draw it where it IS now

	ReleaseDC(hWnd, hdc);
    }
}


void
OnLButtonUp(HWND hWnd, int xClient, int yClient, UINT keyflags)
{
    if (dbgstate.splitflag) {

	// Draw the bar again to erase it
	HDC hdc = GetWindowDC(hWnd);
//	POINT pt = { xClient, dbgstate.splitpos };	// DELETED FJS
	int splitheight = (dbgstate.splitrange.bottom - dbgstate.splitrange.top);
	POINT pt; // FJS
	pt.x = xClient; pt.y = dbgstate.splitpos; // FJS

	ClampSplitter(&pt);

	if (splitheight > 0)
	    dbgstate.splitpercent = (100*(pt.y - dbgstate.splitrange.top))
				  / splitheight;
	else
	    dbgstate.splitpercent = 50;

	RemapSplitRect(hWnd, &pt);	// convert client coords to screen coordinates

	dbgstate.splitflag = FALSE;	// set dragging to unactive

	ReleaseCapture();		// release the mouse capture

	DrawXorBar(hdc, pt.y);
	ReleaseDC(hWnd, hdc);

	// turn off spitter cursor
	SetCursor(winstate.hArrowCursor);
	dbgstate.splitcursor = FALSE;

	// now recompute window geometry
	RecomputeWindows();
    }
}


void
DrawXorBar(HDC hdc, int pos)
{
    static WORD _dotPatternBmp[8] = { 0x00aa, 0x0055, 0x00aa, 0x0055,
				      0x00aa, 0x0055, 0x00aa, 0x0055};
    RECT bar;

    HBITMAP hbm;
    HBRUSH  hbr, hbrushOld;

    bar.top    = pos - SPLITTER_HEIGHT/2;
    bar.left   = dbgstate.splitbar.left;
    bar.right  = dbgstate.splitbar.right;
    bar.bottom = pos + SPLITTER_HEIGHT/2;

    // create a monochrome checkered pattern
    hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);

    hbr = CreatePatternBrush(hbm);

    SetBrushOrgEx(hdc, bar.left, bar.top, 0);
    hbrushOld = (HBRUSH)SelectObject(hdc, hbr);

    // draw the checkered rectangle to the screen
    PatBlt(hdc, bar.left, bar.top, bar.right-bar.left, SPLITTER_HEIGHT, PATINVERT);

    SelectObject(hdc, hbrushOld);

    DeleteObject(hbr);
    DeleteObject(hbm);
}


// the user has requested that the debug window be closed
static void 
OnClose(HWND hwnd)
{
    close_debugwin();
    //WinUpdateToolBar();
}


// destroy the debugger window
static void 
OnDestroy(HWND hwnd)
{
    if (dbgstate.cour_font != NULL) {
	DeleteObject(dbgstate.cour_font);
	dbgstate.cour_font = NULL;
    }
}


// debug window handler
static LRESULT CALLBACK 
WndProcDbg(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch (iMsg) {
	HANDLE_MSG(hWnd, WM_CREATE,  OnCreate);
	HANDLE_MSG(hWnd, WM_SIZE,    OnSize);
	HANDLE_MSG(hWnd, WM_PAINT,   OnPaint);
	HANDLE_MSG(hWnd, WM_LBUTTONDOWN, OnLButtonDown);
	HANDLE_MSG(hWnd, WM_MOUSEMOVE,   OnMouseMove);
	HANDLE_MSG(hWnd, WM_LBUTTONUP,   OnLButtonUp);
	HANDLE_MSG(hWnd, WM_CLOSE,   OnClose);
	HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy);

	case WM_SETFOCUS:
	    SetFocus(dbgstate.hCmdWnd);
	    break;
    }

    return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

 
static void 
RegisterDbgClass(void)
{
    WNDCLASSEX wndclass;

    wndclass.cbSize        = sizeof(wndclass);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wndclass.lpfnWndProc   = WndProcDbg;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = winstate.hInst;
    wndclass.hIcon         = LoadIcon(winstate.hInst, "altairicon");
    wndclass.hCursor       = NULL; //LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = "SolaceDbgWindow";
    wndclass.hIconSm       = LoadIcon(winstate.hInst, "altairicon");

    RegisterClassEx(&wndclass);
}


// indicate if the debugger window is visible or not
// (independent of whether it is active or not)
int
DebuggerWindowVisible(void)
{
    return IsWindow(winstate.hDbgWnd) &&
	   IsWindowVisible(winstate.hDbgWnd);
}


// get the window size and placement.
// return 0 if it isn't valid, 1 if it is valid.
int
DebuggerWindowGetSize(RECT *rc, int *visible)
{
    if (!dbgstate.valid_position || (winstate.hDbgWnd==NULL))
	return 0;

    GetWindowRect(winstate.hDbgWnd, rc);
    *visible = DebuggerWindowVisible();
    return 1;
}

// set the window size and placement.
void
DebuggerWindowSetSize(RECT *rc)
{
    dbgstate.valid_position = TRUE;
    dbgstate.position = *rc;

    if (winstate.hDbgWnd)
	MoveWindow(winstate.hDbgWnd, rc->left, rc->top,
		   (rc->right - rc->left), (rc->bottom - rc->top),
		   TRUE);
}


// if the debug window doesn't exist, create it.  if it exists,
// make sure all controls are reactivated.
static void
activate_debugwin(void)
{
    if (winstate.hDbgWnd == NULL)
    	winstate.hDbgWnd = CreateDbgWindow();		// create debug window

    ShowWindow(winstate.hDbgWnd, SW_SHOW);

    dbgstate.active = TRUE;

    // enable subwindows
    InvalidateRect(dbgstate.hRegWnd, NULL, TRUE);
    DasmWindowAction(WA_ENABLE, 0);
    MemWindowAction(WA_ENABLE, 0);
    LogWindowAction(WA_ENABLE, 0);

    // place current SP in middle of memory window
    MemWindowAction(WA_GOTO, I80Regs.nSP.W);

    update_debugwin(UPDATE_ALL);
    //WinUpdateToolBar();
    SetFocus(dbgstate.hCmdWnd);	// cmd edit window gets focus
}


// inactivate all the subwindows
static void 
inactivate_debugwin(void)
{
    if ((winstate.hDbgWnd == NULL) || !dbgstate.active)
	return;

    dbgstate.active = FALSE;

    // disable subwindows
    InvalidateRect(dbgstate.hRegWnd, NULL, TRUE);
    DasmWindowAction(WA_DISABLE, 0);
    MemWindowAction(WA_DISABLE, 0);
    // LogWindowAction(WA_DISABLE, 0);  nah, leave it going

    // since program is running again, move focus to main window
    SetFocus(winstate.hWnd);
}


static void 
close_debugwin(void)
{
    // just hide the window, don't destroy it
    if (winstate.hDbgWnd == NULL)
	return;

    ShowWindow(winstate.hDbgWnd, SW_HIDE);
    dbgstate.active = FALSE;

    //WinUpdateToolBar();

	// Prevents starting execution when closing the debugger
    //Sys_DbgNotify(FORCERUN_KEY, 0);
}


static void 
update_debugwin(int update_flags)
{
    if (winstate.hDbgWnd == NULL)
	return;

    if (update_flags & HOME_DASM)
	DasmWindowAction(WA_GOTO, I80Regs.nPC.W);
    if (update_flags & HOME_MEM)
	MemWindowAction(WA_GOTO,  I80Regs.nSP.W);

    if (update_flags & UPDATE_REG)
		InvalidateRect(dbgstate.hRegWnd,  NULL, TRUE);
    if (update_flags & UPDATE_DASM) {
		DasmWindowAction(WA_DASMREFRESH, (word)0);
		// make sure the current op is still on screen
		DasmWindowAction(WA_GOTO, I80Regs.nPC.W);
		RecomputeHScrollbarDasm();
		InvalidateRect(dbgstate.hDasmWnd, NULL, TRUE);
    }
    if (update_flags & UPDATE_MEM)
		InvalidateRect(dbgstate.hMemWnd,  NULL, TRUE);
    if (update_flags & UPDATE_CMD)
		InvalidateRect(dbgstate.hCmdWnd,  NULL, TRUE);
    if (update_flags & UPDATE_LOG)
		InvalidateRect(dbgstate.hLogWnd,  NULL, TRUE);

    UpdateWindow(winstate.hDbgWnd);		// cause redraw of everything
}


// like update_debugwin(), but used in single-stepping situations
// to prevent redrawing all windows (less flashing)
static void 
activate_debugwin_step(void)
{
    if (winstate.hDbgWnd == NULL)
		return;

    MemWindowAction(WA_GOTO,  I80Regs.nSP.W);
    DasmWindowAction(WA_GOTO, I80Regs.nPC.W);

    InvalidateRect(dbgstate.hRegWnd,  NULL, TRUE);
    InvalidateRect(dbgstate.hDasmWnd, NULL, TRUE);
    InvalidateRect(dbgstate.hMemWnd,  NULL, TRUE);
    UpdateWindow(winstate.hDbgWnd);		// cause redraw of everything

    SetFocus(dbgstate.hCmdWnd);	// cmd edit window gets focus
}


// indicate if debugger window is active or not
int
UI_DbgWinActive(void)
{
    return dbgstate.active;
}


void
UI_DbgWin(DbgWinMsg_t msg, int arg)
{
    switch (msg) {
	case DW_Activate:     activate_debugwin();      break;
	case DW_ActivateStep: activate_debugwin_step(); break;
	case DW_Inactivate:   inactivate_debugwin();    break;
	case DW_Close:        close_debugwin();         break;
	case DW_Update:       update_debugwin(arg);     break;
	default:
	    ASSERT(0);
    }
}

// ==================================================================
// this is called once at program start-up time
// ==================================================================
void 
InitDebugger(void)
{
    dbgstate.active          = FALSE;
    dbgstate.valid_position  = FALSE;
    dbgstate.cour_font       = NULL;
    dbgstate.hDasmWnd        = 0;
    dbgstate.hRegWnd         = 0;

    InitEditHist();

    // clear out the overlays
    OvlInit();

    DasmWindowAction(WA_INIT, 0);
    MemWindowAction(WA_INIT, 0);
    LogWindowAction(WA_INIT, 0);

    RegisterDbgClass();		// register main debugger window

    RegisterRegClass();		// register main debugger window
    RegisterDasmClass();	// register disassembler window
    RegisterMemClass();		// register memory display window
    RegisterCmdClass();		// register command input window
    RegisterLogClass();		// register command output window
}

// called when we're shutting down -- release any resources
void
DestroyDebugger(void)
{
	OvlCleanup();

	if (winstate.hDbgWnd == NULL)
		return;	// container window already destroyed

	// Destroy continer window. Hopefully won't have to destroy individual
	// child windows dbgstate.hRegWnd, .hMemWnd, .hDasmWnd, .hCmdWnd, .hLogWnd.
	DestroyWindow(winstate.hDbgWnd);
}
/* end of file: windbg.c */
