/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222

**********************************************************************

ASMWND.C defines the assembly language window procedure and various
things that go with it.

**********************************************************************

*/
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>

#include "header.h"
#include "operands.h"
#include "opcodes.h"
#include <ctype.h>

#define ASM_OFFSET 35

#define LINES 1024
#define LINELEN 100

extern HINSTANCE hInstance ;
extern HWND hwndClient,hwndStatus,hwndFrame ;
extern int childxpos, childypos ;
extern PROCESS DebugProcess ;
extern enum DebugState uState;
extern CONTEXT StoppedRegs ;

HWND hwndASM;
HBRUSH pcBrush, stopBrush ;
static HBITMAP pcBitmap, stopBitmap ;
static HWND hwndCtrl, hwndBlank ;

static char **lines ;
static int *addrs ;
static int curline, shownLine ;

static char szASMClassName[] = "xccASMClass";
static char szASMBlankClassName[] = "xccASMClass2";
static int asmAddress = 0x410000, asmIP ;
static DWORD threadID = 0 ;
static int rePosition = 0 ;
static LOGFONT fontdata = {
	16,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
	CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE,
	"Courier New"
} ;
static HFONT asmFont ;
static int caretX,caretY ;
	static int charwidth ;

extern long code_address;
BYTE *code_ptr;
BOOL GetCodeLine(char * s)
{
    BOOL isNewLine = TRUE;
       BYTE *oldposition = code_ptr;
	    char *put;
		OPTABLE *theTable;
    s[0] = 0;
		sprintf(s,"%08x: ", (int) (code_address));
		put = TabTo(s,11);
		code_ptr = ReadOverrides(code_ptr, VAL_386 | VAL_FLOAT);
		if (!(theTable = FindOpcode(code_ptr, VAL_386 | VAL_FLOAT))) {
			strcpy(put,"db");
			put = TabTo(put,TAB_ARGPOS);
			sprintf(put,"%02x", *code_ptr++);
		}
		else {
		      code_ptr = DispatchOperand(code_ptr, theTable, TRUE);
		      FormatDissassembly( put);
		}
		code_address += code_ptr - oldposition;
}
void CalculateDisassembly(int moving)
{
   RECT r ;
	int rv = 0 ;
   int lineno ;
	char buffer[256 ];
   char buf1[256] ;
   BYTE dbuf[32] ;
   int i ;
   int linecount ;
   GetClientRect(hwndBlank, &r) ;
   linecount = (r.bottom - r.top)/16 ;
	code_ptr = dbuf ;

	i = 0 ;
   code_address = asmAddress - LINES/2 ;
   while (code_address < asmAddress + LINES/2 && i < LINES) {
      code_ptr = dbuf ;
      if (code_address >= asmAddress && !rv)
         rv = i ;
      if (!ReadProcessMemory(DebugProcess.hProcess, (LPVOID) (code_address) ,dbuf,32,0)) {
         addrs[i] = code_address ;
         sprintf(lines[i++],"%08X: ??",code_address++) ;
      } else {
         DEBUG_INFO *dbg ;
         char *types, *syms ;
         int offset ;
         if (FindSymbolByAddress(&dbg, &types, &syms,&offset,code_address,buffer)) {
            addrs[i] = code_address ;
            strcat(buffer,":") ;
            strcpy(lines[i++],buffer) ;
         }
         if (GetBreakpointLine(code_address,buf1,&lineno) == code_address) {
            addrs[i] = code_address ;
            sprintf(lines[i++],"MODULE: %s LINE: %d",buf1,lineno) ;
         }
         addrs[i] = code_address ;
         GetCodeLine(lines[i++]);
      }
   }
   if (!moving) {
      shownLine = rv - linecount/2 ;
   } else {
      shownLine = rv ;
      if (moving > 0)
         while (addrs[shownLine] == addrs[shownLine+1])
            shownLine++ ;
      shownLine += moving ;
   }
   while (shownLine && addrs[shownLine-1] == addrs[shownLine])
      shownLine-- ;      
   asmAddress = addrs[shownLine] ;
}
void DoDisassembly(HDC dc, RECT *r)
{
	int i ;
   COLORREF v ;
	int linecnt = (r->bottom - r->top)/16 ;
	for (i=0; i < linecnt; i++) {
		int n ;
		RECT r1 ;
		sscanf(lines[i+shownLine], "%x",&n) ;
      if (addrs[i+shownLine] == addrs[i+shownLine+1]) {
         v = SetTextColor(dc,0xcc00) ; 
      }
      TextOut(dc,ASM_OFFSET+5,i*16+r->top,lines[i+shownLine],strlen(lines[i+shownLine])) ;
      if (addrs[i+shownLine] == addrs[i+shownLine+1]) {
         SetTextColor(dc,v) ; 
      }
		if (n == asmIP && (uState == atException || uState == atBreakpoint)) {
			r1.left =  16 ;
			r1.top = i*16 ;
			r1.right = r1.left + 16 ;
			r1.bottom = r1.top + 16 ;
			FillRect(dc,&r1, pcBrush) ;
		}
		if (isBreakPoint(n)) {
         r1.left =  0 ;
			r1.top = i*16 ;
			r1.right = r1.left + 16 ;
			r1.bottom = r1.top + 16 ;
			FillRect(dc,&r1, stopBrush) ;
		}
		
	}
}
void doPaint(HWND hwnd)
{
	HDC dc ;
	HPEN hpen,oldpen ;
	RECT r ;
	HBRUSH graybrush ;
	PAINTSTRUCT paint ;
	HFONT oldFont ;
	int oldcolor ;
			GetClientRect(hwnd,&r) ;
			dc = BeginPaint(hwnd,&paint) ;
			hpen = CreatePen(PS_SOLID,1,0xcccccc) ;
         graybrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE)) ;
			r.right = ASM_OFFSET-3;
			MoveToEx(dc, ASM_OFFSET-1,0,0) ;
			LineTo(dc,ASM_OFFSET-1, r.bottom) ;
			FillRect(dc,&r,graybrush) ; 
			oldpen = SelectObject(dc,hpen) ;
			MoveToEx(dc,ASM_OFFSET-2,0,0) ;
			LineTo(dc,ASM_OFFSET-2,r.bottom) ;
			SelectObject(dc,oldpen) ;
			DeleteObject(hpen) ;

			if (DebugProcess.hProcess) {
				oldFont = SelectObject(dc,asmFont) ;
				oldcolor = SetTextColor(dc,0xff0000) ;
				DoDisassembly(dc,&r) ;
				SetTextColor(dc,oldcolor) ;
				SelectObject(dc,oldFont) ;
			}
			EndPaint(hwnd,&paint) ;
}

LRESULT  CALLBACK _export ASMBlankProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	RECT r ;
	int xlines ;
   switch(iMessage) {
      case WM_CREATE:
			SetScrollRange(hwnd,SB_VERT,0,64000,FALSE );
			SetScrollPos(hwnd,SB_VERT,32000,TRUE) ;
         break ;
		case WM_PAINT:
			doPaint(hwnd) ;
			return 0 ;
		case WM_SETFOCUS:
			CreateCaret(hwnd,0,2,16) ;
			SetCaretPos(caretX,caretY) ;
			ShowCaret(hwnd) ;
			break ;
		case WM_KILLFOCUS:
			DestroyCaret() ;
			break ;
      case WM_RBUTTONDOWN:
		case WM_LBUTTONDOWN:
			caretX = LOWORD(lParam) ;
			caretY = HIWORD(lParam) ;
			if (LOWORD(lParam) >= ASM_OFFSET + 5) {
				caretY &= ~15 ;
				caretX -= ASM_OFFSET + 5 ;
				caretX /= charwidth ; caretX *= charwidth ;
				caretX += ASM_OFFSET + 5 ;
				SetCaretPos(caretX, caretY) ;
			}
         SetFocus(hwnd) ;
         break ;
      case WM_KEYDOWN:
         switch(wParam) {
            case VK_UP:
               SendMessage(hwnd,WM_VSCROLL,SB_LINEUP,0) ;
               break ;
            case VK_DOWN:
               SendMessage(hwnd,WM_VSCROLL,SB_LINEDOWN,0) ;
               break ;
            case VK_PRIOR:
               SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,0) ;
               break ;
            case VK_NEXT:
               SendMessage(hwnd,WM_VSCROLL,SB_PAGEDOWN,0) ;
               break ;
            case VK_HOME:
               SendMessage(hwnd,WM_VSCROLL,SB_TOP,0) ;
               break ;
            case VK_END:
               SendMessage(hwnd,WM_VSCROLL,SB_BOTTOM,0) ;
               break ;
         }
         break ;
		case WM_VSCROLL:
			GetClientRect(hwnd,&r) ;
			xlines = r.bottom/16 ;
			switch(LOWORD(wParam)) {
				case SB_BOTTOM:
				case SB_TOP:
					asmAddress = asmIP ;
               CalculateDisassembly(0) ;
					break ;
				case SB_ENDSCROLL:
					return 0 ;
				case SB_LINEDOWN:
               CalculateDisassembly(1) ;
					break ;
				case SB_LINEUP:
               CalculateDisassembly(-1) ;
					break ;
				case SB_PAGEDOWN:
               CalculateDisassembly(xlines-1) ;
					break ;
				case SB_PAGEUP:
               CalculateDisassembly(1-xlines) ;
					break ;
				case SB_THUMBPOSITION:
					break ;
				case SB_THUMBTRACK :
					return 0 ;
//					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
			}
			InvalidateRect(hwnd,0,1) ;
//			SetScrollPos(hwnd,SB_VERT, 32000,TRUE) ;
			return 0 ;
   }
      
	return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
}
LRESULT  CALLBACK _export gotoProc2( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	char buf[256] ;
	int i ;
   VARINFO *info ;
   char *types, *syms ;
   int offset=StoppedRegs.Eip;
   DEBUG_INFO * dbg ;
	switch(iMessage) {
		case WM_COMMAND:
			if (wParam == IDOK) {
				GetEditField(hwnd,IDC_GOTO,buf);
            info = EvalExpr(&types,&syms, &dbg,&offset, buf) ;
            if (info) {
               if (info->constant)
                  i = info->ival ;
               else
                  i = info->address ;
               FreeVarInfo(info) ;
            } else
               i = 0xffffffff ;
				EndDialog(hwnd,i) ;
            break ;
			}
         if (HIWORD(wParam) == EN_CHANGE) {
               DisableControl(hwnd,IDOK,!GetWindowText((HWND)lParam,buf,2)) ;
               break ;
         }
         if (wParam != IDCANCEL)
            break ;
		case WM_CLOSE:
			EndDialog(hwnd,0xffffffff) ;
			break ;
		case WM_INITDIALOG:
			CenterWindow(hwnd) ;
			SetEditField(hwnd,IDC_GOTO,"") ;
         DisableControl(hwnd,IDOK,1) ;
			break ;
	}
	return 0 ;
}
LRESULT  CALLBACK _export ASMProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	RECT r ;
	int i ;
	HDC dc ;
	TEXTMETRIC metric ;
	DEBUG_EVENT *xc ;
	HFONT oldFont ;
	switch(iMessage) {
		case WM_SYSCOMMAND :
			if (wParam == SC_CLOSE)
				SendMessage(hwnd,WM_CLOSE,0,0) ;
			break ;
      case WM_SETFOCUS:
         SetFocus(hwndBlank) ;
         break ;
		case WM_COMMAND:
			switch(LOWORD(wParam)) {
            case IDM_GOTO:
               asmAddress = DialogBox(hInstance,"GOTODIALOG2",hwnd,(DLGPROC)gotoProc2) ;
               if (asmAddress != 0xffffffff) {
                  CalculateDisassembly(0) ;
                  InvalidateRect(hwndBlank,0,1) ;
               }
					break ;
				case ID_SETADDRESS:
					xc = (DEBUG_EVENT *)lParam ;
					asmAddress = asmIP = xc->u.Exception.ExceptionRecord.ExceptionAddress ;
					threadID = xc->dwThreadId ;
               CalculateDisassembly(0) ;
               InvalidateRect(hwndBlank,0,1) ;
					break ;
				default:
					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
			}
			break ;
		case WM_GETCURSORADDRESS:
			if (shownLine + caretY/16 >=4096)
				return 0 ;
			sscanf(lines[shownLine+caretY/16],"%x",&i) ;
			return i ;
		case WM_CREATE:
         lines = (char **)malloc(LINES * sizeof(char *)) ;
         if (!lines)
            return -1 ;
         addrs = (int *)malloc(LINES *sizeof(int)) ;
         if (!addrs)
            return -1 ;
         for (i=0; i < LINES; i++)
            if (!(lines[i] = (char *)malloc(LINELEN)))
               return -1 ;
			caretY = 0 ;
			caretX = ASM_OFFSET + 5 ;
			hwndASM = hwnd ;
			asmFont = CreateFontIndirect(&fontdata) ;
			dc = GetDC(hwnd) ;
			oldFont = SelectObject(dc, asmFont) ;
			GetTextMetrics(dc,&metric) ;
			SelectObject(dc,oldFont) ;
			ReleaseDC(hwnd,dc) ;
			charwidth = metric.tmMaxCharWidth ;
         GetClientRect(hwnd,&r) ;
         hwndCtrl = CreateControlWindow(DID_ASMWND,hwnd,&r,(int)((LPMDICREATESTRUCT)(*(int *)lParam))->lParam) ;
         SendMessage(hwndCtrl,LCF_ADJUSTRECT,0,(LPARAM)&r) ;
         hwndBlank = CreateWindow(szASMBlankClassName,0,
            WS_CHILD + WS_VSCROLL + WS_CLIPSIBLINGS + WS_BORDER + WS_VISIBLE,
            r.left,r.top,r.right-r.left,r.bottom-r.top,
            hwndCtrl,0,hInstance, 0) ;
         break ;
				
		case WM_CLOSE:
         dmgrHideWindow(DID_ASMWND,TRUE );
			return 0 ;
			break ;
		case WM_DESTROY:
         dmgrRemoveClient((CCW_params *)GetWindowLong(hwndCtrl,0)) ;
         DestroyWindow(hwndBlank) ;
			hwndASM = 0 ;
			DeleteObject(asmFont) ;
         for (i=0; i < LINES; i++)
				free(lines[i]) ;
         free(lines) ;
         free(addrs) ;
			break ;
		case WM_SIZE:       
         r.left = r.top = 0 ;
         r.right = LOWORD(lParam) ;
         r.bottom = HIWORD(lParam) ;
         MoveWindow(hwndCtrl,0,0,r.right, r.bottom, TRUE) ;
         SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0 , (LPARAM)&r ) ;
         MoveWindow(hwndBlank,r.left,r.top,r.right-r.left,r.bottom-r.top,TRUE );
			break ;
		default: 
			break ;
	}
	return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
}
void RegisterASMWindow(void)
{
		WNDCLASS wc ;
      LOGBRUSH brushstr ;
      memset(&wc,0,sizeof(wc)) ;
      wc.style = 0 ;
		wc.lpfnWndProc = &ASMProc ;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = hInstance ;
		wc.hIcon = LoadIcon(0,IDI_APPLICATION) ;
		wc.hCursor = LoadCursor(0,IDC_ARROW) ;
		wc.hbrBackground = GetStockObject(WHITE_BRUSH) ;
		wc.lpszMenuName = 0 ;
		wc.lpszClassName = szASMClassName ;
		RegisterClass(&wc) ;

      wc.lpfnWndProc = & ASMBlankProc ;
      wc.lpszClassName = szASMBlankClassName ;
      RegisterClass(&wc) ;

      pcBitmap = LoadBitmap(hInstance, "ID_PCBMP") ;
      stopBitmap = LoadBitmap(hInstance, "ID_STOPBMP") ;
      ChangeBitmapColor(pcBitmap,0xffffff,GetSysColor(COLOR_3DFACE)) ;
      ChangeBitmapColor(stopBitmap,0,GetSysColor(COLOR_3DFACE)) ;
      brushstr.lbStyle = BS_PATTERN ;
      brushstr.lbHatch = (int) pcBitmap ;
      pcBrush = CreateBrushIndirect(&brushstr) ;   
      brushstr.lbHatch = (int)stopBitmap ;
      stopBrush = CreateBrushIndirect(&brushstr) ;
}
HWND CreateASMWindow(void)
{
	MDICREATESTRUCT mc ;
	HWND rv ;
	if (hwndASM) {
		SendMessage(hwndASM,WM_SETFOCUS,0,0) ;
		return hwndASM ;
	}
	mc.szClass = szASMClassName ;
	mc.szTitle = "Assembly Window" ;
	mc.hOwner = hInstance ;
	mc.x = childxpos ;
	mc.y = childypos ;
   mc.cx = 500;
   mc.cy = 200;
   mc.style = WS_CHILD | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_CLIPCHILDREN;
   mc.lParam = (LPARAM)0 ;
	rv = (HWND) SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)&mc) ;
	childxpos += 20 ;
	childypos += 20 ;
	if (childxpos > 120)
		childxpos = childypos = 0 ;
	return rv ;
}