
/***************************************************************************
**
**    $Header:   D:/ECB2S/SRC/LOG/EMUSTEPF.CPP   1.2.1.5   17 Apr 1997 15:48:54   ZJRD  $
**
**    $Log:   D:/ECB2S/SRC/LOG/EMUSTEPF.CPP  $
** 
**    Rev 1.2.1.5   17 Apr 1997 15:48:54   ZJRD
** No change.
** 
**    Rev 1.2.1.4   16 Apr 1997 10:20:12   ZJRD
** No change.
** 
**    Rev 1.2.1.3   10 Apr 1997 15:31:34   ZJRD
** 2.09F
** 
**    Rev 1.2.1.1   03 Apr 1997 15:12:20   ZJRD
** No change.
** 
**    Rev 1.2.1.0   28 Mar 1997 10:17:22   ZJRD
** easy pack sld 2.09d
** 
**    Rev 1.1   19 Mar 1997 11:28:28   ZJRD
** No change.
** 
**    Rev 1.0   12 Mar 1997 14:46:10   ZJRD
** Initial revision.
** 
****************************************************************************/

/***************************************************************************
// file name: emustepa.cpp
//
// Author: Frank Chang
//
// Note:   Replace emustep.cpp of epsld project to improve performance of
//		   C source level step.  12/18/96, chris.
// Version 01: initial version,
//       Date: 12/28/95 -- 12/29/95
// Vesrion 02: solve "switch-case" structure, not accomplished
//       Date: 12/29/95 -- 01/02/96
// Version 03: add more detail analysis in IsNoJumpCmds().
//       Date: 12/29/95 -- 01/02/96
// Version 04: solve "switch case" structure, accomplished.
//       Date: 12/29/95 -- 01/03/96
// Version 05: fasten "step" and "step over"
//       Date: 12/29/95 -- 01/04/96
// Version 06: add check CPU status in EmuFreeRun()
//       Date: 12/29/95 -- 01/02/96
//
// Version 07: purpose: only check one time, not run cycle. So IsNoJumpCmds
//             can be called only once.
//             1. Modify IsNoJumpCmds() to AnalyseCmdSet()
//       Date: 12/29/95 -- 01/02/96
// modify record:
//		1.	add AsmStepInto(...) member function,
//			modify AsmStepOut(...) member function, to implement
//			step (over) till call,
//			step (over) till return,
//			step (over) into call,
//			step (over) over return	in instruction mode.
//			by chris, 12/19/96
//
// State: not accomplished
//
// description: to complish source level "step" series commands
//
// Accomplishment  Analysis: From A to E
//
// A. C source object file
     1. C source mode, step
       1. analysis?
         1. no, give up.
         2. yes:
         ** 1. just one instruction
                1. is call(only check one byte),
                    is lib_func, set bp at NEXT_ADDR + END_BP, go and continue
                    is not lib_func, AbiStepOne and stop.
                2. is "djnz"
                    set bp at NEXT_ADDR (suppose) + END_BP
                3. others, one by one
                notes:
                  for "jump"
                  need analysis dest address? No, set bp at jump instruction itself.
                  yes,  ( is outside, yes, set bp at destAddr) not sure
            2. entire instructions
         3. assistant: END_BP

     2. C source mode, step over
       1. analysis?
         1. no, give up.
         2. yes:
         ** 1. just one instruction
                1. is call(only check one byte),
                    is lib_func, set bp at NEXT_ADDR + END_BP, go and continue
                    is not lib_func, same with lib_func.
                2. is "djnz"
                    set bp at NEXT_ADDR (suppose) + END_BP
                3. others, one by one
                notes:
                  for "jump"
                  need analysis dest address? No, set bp at jump instruction itself.
                  yes,  ( is outside, yes, set bp at destAddr) not sure
            2. entire instructions
         3. assistant: END_BP

    3. Mixed mode, step
       same with 5

    4. Mixed mode, step over
       same with 6

    5. Assembly mode, step
       1. AbiStepOne

    6. Assembly mode, step over
        1. is call(only check one byte),
            is lib_func, set bp at NEXT_ADDR + END_BP, go then stop
        2. others, one by one, AbiStepOne

// B. Assembly source object file
     1. step
       1. AbiStepOne
     2. step over
       1. current SP level, if not call instructions(include acall & lcall),
          AbiStepOne
       2. if is call instructions, need analysis
       purpose: avoid use "pop dph; pop dpl; jmp @a+dptr" instead of "ret" to cause
                "ep running", or user's duty?
       1. try to avoid
           original: only set bp at next instruction
           need:  add 3 bp after
           method:  1. step analysis,  no need
                    2. entire analysis?, no need
           conclusion: user's duty
       2. user's duty


// C. Source window ONLY provide Instructions, they are:
        1. Step
        2. Step over
        3. Step till ret

// D. Command line provide such commands:
        1. Step
        2. Step over
        3. Step till ret

// E: Notes:
       END_BP: add bp at next statement start address

////////////////////////
//
// Background Information
// 01. Gates said on 12/25/95, Information provided by GetStatementRange()
//     is more detail than which provided by GetLineRange(). for instance,
//     if compiler is Franklin C(no statement information)
// 02. A result: Compiler will use:
//       01. pop dph (machine code: 0xd083)
//       02. pop dpl (machine code: 0xd082)
//       03. jmp @a+dptr (0x73)
//     instead of "ret" to echo "LCALL #?C_XXXXXX"
//     Because F/W only check "ret", F/W will hange while meeting such commands.
// 03. We can omit the "call" kind commands if the called function is a
//     library function. In short words, we cannot(need not) set bp.
// 04. if called function is a sub routine with source info, "step over" will
//     omit while "step" will go into and stop.
// 05. control and jump commands
//       01. acall addr11, a10a9a8(10001) a7--a0,   2 bytes
//       02. lcall addr16, 0x12 a15--a0,            3 bytes
//       03. ret 0x22,                              1 byte
//       04. reti 0x32,                             1 byte
//       05. ajmp addr11, a10a9a8(00001) a7--a0,    2 bytes
//       06. ljmp addr16, 0x2 a15--a0,              3 bytes
//       07. sjmp rel(-128~127), 0x80 rel7--rel0,   2 bytes
//       08. jmp @a+dptr, 0x73,                     1 byte
//       09. jz rel, 0x60 rel7--rel0,               2 bytes
//       10. jnz rel, 0x70 rel7--rel0,              2 bytes
//       11. jc rel, 0x40 rel7--rel0,               2 bytes
//       12. jnc rel, 0x50 rel7--rel0,              2 bytes
//       13. jb bit, rel, 0x20 baddr, rel7--rel0,   3 bytes
//       14. jnb bit, rel, 0x30 baddr, rel7--rel0,  3 bytes
//       15. jbc bit, rel, 0x10 baddr, rel7--rel0,  3 bytes
//       16. cjne a, direct, rel, 0xb5 direct rel,  3 bytes
//       17. cjne a, #data, rel, 0xb4 #data rel,    3 bytes
//       18. cjne rn, #data, rel, (0xb8--0xbf) #data rel,  3 bytes
//       19. cjne @ri, #data, rel, (0xb6--0xb7) #data rel, 3 bytes
//       20. djnz rn, rel, (0xd8--0xdf) rel,        2 bytes
//       21. djnz direct, rel, 0xd5 direct rel,     3 bytes
//
//    {
//    static struct judge_tag {
//        unsigned char chCmdId;
//        unsigned char chCmdLen;
//    } judgeArray[] = {       //01, 05, 16-20
//            0x12,   3,   // lcall
//            0x22,   1,   // ret
//            0x32,   1,   // reti
//            0x02,   3,   // ljmp
//            0x80,   2,   // sjmp
//            0x73,   1,   // jmp
//            0x40,   2,   // jc
//            0x50,   2,   // jnc
//            0x60,   2,   // jz
//            0x70,   2,   // jnz
//            0x10,   3,   // jbc
//            0x20,   3,   // jb
//            0x30,   3,   // jnb
//            0xd5,   2,   // djnz
//    };
//
//
***************************************************************************/

                /*********************************
                **                              **
                **      include file            **
                **                              **
                *********************************/

#include "stdafx.h"
#include "abibase.h"
#include "uicom.h"

#include "emustep.h"

extern int nMaxReg;
extern char * RegName[128];
///////////////////////////////////////////////////////////
// extern function
extern int TestKey(WORD wKey);

//the following functions are provide by ABI
//
extern STATUS AbiGo(FLAG runFlag, ADDR fromAddr,ADDR tillAddr);
extern STATUS AbiStepOne(); 
extern STATUS AbiGetCpuStatus(UINT *);
extern STATUS AbiSetBp(ADDR);
extern STATUS AbiClrBp(ADDR); 
extern STATUS AbiGetOneReg(int iRegId, UINT* uRegValue);
extern STATUS AbiGetMemN(ADDR addr1, ADDR addr2,char* pchBuff);


//the following functions are provided by John
extern unsigned char TRCGetInstLen(unsigned char code);
extern BOOL BptSearchBpt(unsigned short addr,BYTE &eFlag);

//the following functions are provided by Gates

extern void ShowLine(char *psz);
extern int GetStepOption(void); // return,  0 --ASM, 1 --Statement,  2 --Line
extern int GetLibRange(unsigned short uAddr,
    unsigned short& uStart, unsigned short& uEnd);
extern int FrankGetFuncRange(ULONG addr , ULONG& uStart , ULONG& uEnd);

extern "C" {
    void GetStatementRange(unsigned short uAddr,
                              unsigned short& uStart, unsigned short& uEnd);
    BOOL IsLibFunction(unsigned short uAddr);
}

CEmuStepServer objEmuStep;
//////////////////////////////////////////////////////////////////////////
// Interface Function
//
// these two function will be called by "source" window
void EmuServerStep(long lCount);
void EmuServerStepOver(long lCount);

// this function will be called by "step" command line
void StepCmd(int nArgc, char* pszArgv[]);

//////////////////////////////////////////////////////////////////////////
// Local Function
//
void StepTillDestReg(int nRegId, WORD wRegValue);
void StepTillDestAddr(ADDR addr, WORD wAddrValue);

BOOL StrToAddr(char * psz, PADDR & paddr);

BOOL StrToAddr(char * psz, ADDR & addr)
{
    char *pch;
    char addrType;
    DWORD dwAddr;

    if ( psz[1] == ':') {
        if (psz[0] == 'P' || psz[0] == 'p' )
            addrType = 1;
        else if (psz[0] == 'X' || psz[0] == 'x' )
            addrType = 2;
        else if (psz[0] == 'I' || psz[0] == 'i' )
            addrType = 3;
        else if (psz[0] == 'R' || psz[0] == 'r' )
            addrType = 4;
        else if (psz[0] == 'B' || psz[0] == 'b' )
            addrType = 5;
        else return (FALSE);

        pch = &psz[2];
    }
    else {
        pch = &psz[2];
        addrType = 1;
    }

    Str2Num(pch, dwAddr);

    addr.addrType = addrType;
    addr.addr = (WORD) dwAddr;
    return TRUE;
}

/***************************************************************************
**
**  Name: MakeAbiAddr
**
**  Function: convert unsigned short address position into struct ADDR
**
**
**
***************************************************************************/
ADDR CEmuStepServer::MakeAbiAddr(PADDR nAddr)
{
    ADDR structAddr;
    structAddr.addrType = 0x1;
    structAddr.addr = nAddr;
    return (structAddr);
}

/***************************************************************************
**
**  Name: AnalyseCmdSet
**
**  Function: Check if the machine code is one of the following:
**  Input:  0 -- step
**          1 -- step over
**          2 -- step over return
**          3 -- step into call
**          4 -- step uStart uEnd (after dealing with addr_range, it = 0)
**          5 -- step over uStart uEnd (after dealing with addr_range, it = 1)
**          6 -- step till return
**          7 -- step till call
**
**  Return:
**
***************************************************************************/
STRATAGEM CEmuStepServer::AnalyseCmdSet( int nStepOpt, int nGetType)
{   
    ::AbiGetOneReg(0, (UINT*) &m_uStart);

    if ( 4 != nStepOpt && 5 != nStepOpt ) { //NOT user defined
        if ( OUT_STATEMENT == nGetType )
            ::GetStatementRange(m_uStart, m_uFromAddr, m_uTillAddr);

        else if ( OUT_FUNCTION == nGetType ) {
            if ( 0 != FrankGetFuncRange(m_uStart, (ULONG &) m_uFromAddr,
                                           (ULONG &) m_uTillAddr) ) {
                ::MessageBox( NULL, "The routine's range cannot be gotten!",
                            "Error", MB_OK|MB_ICONASTERISK );
                return JUST_DO_NOTHING;
            }
        }
        else
            return JUST_DO_NOTHING;
    }

    if ( 4 == nStepOpt ) nStepOpt = 0;
    else if ( 5 == nStepOpt ) nStepOpt = 1;

    PADDR uStart;
    // must: m_uStart <= m_uTillAddr
    if ( m_uTillAddr < m_uFromAddr ) {
        ::MessageBox( NULL, 
            "Start address > end address in code information!",
            "Error", MB_OK|MB_ICONASTERISK );
        return JUST_DO_NOTHING;
    }
                                       
    m_nMasnCmdsLen =  m_uTillAddr - m_uFromAddr + 1;

    if ( m_nMasnCmdsLen > 0x400 ) {
       	if (m_uTillAddr - m_uStart + 1 > 0x400) {
       		m_nMasnCmdsLen = 0x400;
       	}  
       	else {
       		m_nMasnCmdsLen = m_uTillAddr - m_uStart +1;
       	}	
       	uStart = m_uStart;     
       	m_fTooBigRoutine = TRUE;
    }    
    else 
    	uStart = m_uFromAddr;
    	
    m_uchMasnCode = new unsigned char [ m_nMasnCmdsLen + 1];
    
    
    if ( m_nMasnCmdsLen <= 0x100) {
        ::AbiGetMemN(MakeAbiAddr(uStart),
                     MakeAbiAddr(m_uTillAddr),
                     (char *) m_uchMasnCode);
    }
    else {
        for (unsigned int i=0; i < m_nMasnCmdsLen; i += 0x100 ) {
            if ( (m_nMasnCmdsLen - i) > 0x100) {
                ::AbiGetMemN(MakeAbiAddr(uStart+i),
                             MakeAbiAddr(uStart+i+0xff),
                             (char *) (m_uchMasnCode+i) );
            }
            else {
                ::AbiGetMemN(MakeAbiAddr(uStart+i),
                             MakeAbiAddr(m_uTillAddr),
                             (char *) (m_uchMasnCode+i));
            }
        }
    }

    unsigned char *uchPtr = m_uchMasnCode;

    //for ASM source file
    if ( (unsigned char) m_nMasnCmdsLen ==
         ::TRCGetInstLen( *uchPtr) ) {
        if ( 2 == nStepOpt || 6 == nStepOpt ) { //ret
            if ( 0x22 != *uchPtr && 0x32 != *uchPtr ) {
                ::MessageBox( NULL, "Cannot get return address!",
                            "Error", MB_OK|MB_ICONASTERISK );
                delete m_uchMasnCode;
                return JUST_DO_NOTHING;
            }    
            else
                return JUST_STEP_ONE;
        }
        else if ( 3 == nStepOpt || 7 == nStepOpt ) { //call
            if ( 0x11 != *uchPtr && 0x12 != *uchPtr ) {
                ::MessageBox( NULL, "Cannot get a sub routine!",
                            "Error", MB_OK|MB_ICONASTERISK );
                delete m_uchMasnCode;
                return JUST_DO_NOTHING;
            }    
            else
                return JUST_STEP_ONE;
        }
        return JUST_STEP_ONE;
    }

    unsigned char  uchOneCmdLen;
    unsigned short un = 0;

    PADDR uDestAddr;

    while ( un < m_nMasnCmdsLen ) {
        uchOneCmdLen = TRCGetInstLen(*uchPtr);
        ASSERT (uchOneCmdLen > 0 && 4 > uchOneCmdLen);

        if ( 0x11 == (*uchPtr & 0x1f) ) { //acall
            if ( 0 == nStepOpt || 3 == nStepOpt || 7 == nStepOpt ) { //step
                uDestAddr = ((uStart + un + 2) & 0xf800) |
                    (((((((WORD)(*uchPtr)&0xe0)<<3) |
                         ((WORD)(*(uchPtr+1)))&0xff) & 0x07ff) ) & 0x7ff);
                if ( !IsLibFunction(uDestAddr) )  { //yes and step
                    if ( 0 != un ) {
                        if ( 7 == nStepOpt )
                            AddIndexNode(uStart + un);
                        else
                            AddIndexNode(uDestAddr);
                    }
                    else return JUST_STEP_ONE;
                }
            }
        }
        else if ( 0x12 == *uchPtr ) { //LCALL
            if ( 0 == nStepOpt || 3 == nStepOpt || 7 == nStepOpt ) { //step
                uDestAddr = (PADDR)
                    ( ((((WORD)(*(uchPtr+1)))<<8)&0xff00) |
                    (((WORD)(*(uchPtr+2)))&0x0ff) );
                if ( !IsLibFunction(uDestAddr) ) { //yes
                    if ( 0 != un ) {
                        if ( 7 == nStepOpt )
                            AddIndexNode(uStart + un);
                        else
                            AddIndexNode(uDestAddr);
                    }
                    else return JUST_STEP_ONE;
                }
            }
        }
        else if ( 0x02 == *uchPtr && ! (2 == nStepOpt || 6 == nStepOpt ||
                                        3 == nStepOpt || 7 == nStepOpt) ) { //ljmp
            uDestAddr = (PADDR)
                ( ((((WORD)(*(uchPtr+1)))<<8)&0xff00) |
                (((WORD)(*(uchPtr+2)))&0x0ff) );
            if ( uDestAddr < uStart || uDestAddr >= (uStart + m_nMasnCmdsLen) )
            {
//                AddIndexNode(m_uStart + un);
                AddIndexNode(uDestAddr);
            }
        }
        else if ( 0x01 == (*uchPtr & 0x1f) &&
                  !(2 == nStepOpt || 6 == nStepOpt ||
                    3 == nStepOpt || 7 == nStepOpt) ) { //ajmp
            uDestAddr = ((uStart + un + 2) & 0xf800) |
                (((((((WORD)(*uchPtr)&0xe0)<<3) |
                     ((WORD)(*(uchPtr+1)))&0xff) & 0x07ff) ) & 0x7ff);
            if ( uDestAddr < uStart || uDestAddr >= (uStart + m_nMasnCmdsLen) )
            {
//                AddIndexNode(m_uStart + un);
                AddIndexNode(uDestAddr);
            }
        }
        else if ( 0x80 == *uchPtr && !(2 == nStepOpt || 6 == nStepOpt ||
                                       3 == nStepOpt || 7 == nStepOpt) ) { //sjmp
            uDestAddr = (uStart + un + 2) + (char)(*(uchPtr+1));
            if ( uDestAddr < uStart || uDestAddr >= (uStart + m_nMasnCmdsLen) )
            {
//                AddIndexNode(m_uStart + un);
                AddIndexNode(uDestAddr);
            }
        }
        else {
            if ( ! (2 == nStepOpt || 6 == nStepOpt ||
                    3 == nStepOpt || 7 == nStepOpt ) &&
                (0x60 == *uchPtr ||			//JZ
                 0x70 == *uchPtr ||			//JNZ
                 0x40 == *uchPtr ||         //JC
                 0x50 == *uchPtr ||         //JNC
                 0x20 == *uchPtr ||         //JB
                 0x30 == *uchPtr ||         //JNB
                 0x10 == *uchPtr ||         //JBC
                 0xd5 == *uchPtr ||         //DJNZ
                 0x73 == *uchPtr ||         //JMP
                (0xb4 <= *uchPtr && 0xbf >= *uchPtr) ||		//CJNE
                (0xd8 <= *uchPtr && 0xdf >= *uchPtr)) )     //DJNZ
            {
                uDestAddr = (uStart + un + uchOneCmdLen) +
                              (char)(*(uchPtr+uchOneCmdLen-1));
                if ( uDestAddr < uStart ||
                     uDestAddr >= (uStart + m_nMasnCmdsLen) )
                {
//                    AddIndexNode(m_uStart + un);
                    AddIndexNode(uDestAddr);
                }
            }
            else if ( !( 3 == nStepOpt || 7 == nStepOpt ) &&
                      ( 0x22 == *uchPtr || 0x32 == *uchPtr) ) {	//RET, RETI
                    AddIndexNode(uStart + un);
            }
        }

        uchPtr += uchOneCmdLen;
        un += (unsigned short) uchOneCmdLen;
    } //end of while

    if ( NULL != indexHead )
        return JUST_GO_TO_FIRST_JUMP;

    else return JUST_GO_OUTSIDE;
}

/***************************************************************************
**
**  Name: IsSpecialMasnCode
**
**  Function: Check if the machine code is one of the following:
**      1. acall, 1
**      2. lcall, 2
**      3. djnz,  3
**      4. ret or reti
**
**  Return: 1 or 2 or 3 or 0
**
***************************************************************************/
int CEmuStepServer::IsSpecialMasnCode( unsigned char uchMasnCode)
{
    if ( (uchMasnCode & 0x1f) == 0x11 ) { //acall addr11
        return 1;
    }
    else if ( uchMasnCode == 0x12 )  { // lcall
        return 2;
    }
    else if ( uchMasnCode >= 0xd8 && uchMasnCode <= 0xdf ) { //djnz rn, rel
        return 3;
    }                 
    else if ( uchMasnCode == 0x22 || uchMasnCode == 0x32 ) {	//RET, RETI
        return 4;
    }
    else return 0;
}

/***************************************************************************
**
**  Name: EmuSetTempBp
**
**  Function: Set temporary breakpoints
**
**  Return: the return code from ABI
**
***************************************************************************/
STATUS CEmuStepServer::EmuSetTempBp(PADDR uAddr)
{
    STATUS status;
    BYTE fEnable;

    if ( !::BptSearchBpt(uAddr, fEnable) ) {
        NODE * pNode = bpHead;
        while ( pNode ) {
            if ( pNode->addr == uAddr ) {
                return ICE_OK;
            }
            else {
                pNode = pNode->next;
            }
        }
        status = ::AbiSetBp(MakeAbiAddr(uAddr));
        if (status == ICE_OK) {
            AddBpNode(uAddr);
        }
        return status;
    }
    return (ICE_OK);
}



BOOL CEmuStepServer::AddBpNode(PADDR uAddr)
{
    static NODE *pPriorNode = 0;

    NODE *pNode = new NODE;
    if ( NULL == pNode ) return(FALSE);

    pNode->addr = uAddr;
    pNode->next = NULL;
    if ( NULL == bpHead ) {
        bpHead = pNode;
    }
    else {
        pPriorNode->next = pNode;
    }
    pPriorNode = pNode;

    return (TRUE);
}

BOOL CEmuStepServer::AddIndexNode(PADDR uAddr)
{
    static NODE *pPriorNode=0;

    NODE *pNode = new NODE;
    if ( NULL == pNode ) return(FALSE);

    pNode->addr = uAddr;
    pNode->next = NULL;
    
    
    if ( NULL == indexHead) {
        indexHead = pNode;
    }
    else {   
        NODE * ppNode = indexHead;
    	while ( ppNode ) {
    		if ( uAddr == ppNode->addr) {
    			delete pNode;
    			return TRUE;
    		}	             
    		ppNode = ppNode->next;
    	}
        pPriorNode->next = pNode;
    }
    pPriorNode = pNode;

    return (TRUE);
}


/***************************************************************************
**
**  Name: EmuClrTempBp
**
**  Function: Clear two links.
**            1. index link
**            2. temporary breakpoints link
**
**  Return: the return code from ABI
**
***************************************************************************/
STATUS CEmuStepServer::EmuClrTempBp()
{
    STATUS status = ICE_OK;

    //1. clear node link

    
    while ( NULL != indexHead ) {
        NODE * pNode = indexHead;
        indexHead = pNode->next;
        delete pNode;
    }

    //2. clear bp link
    while ( NULL != bpHead ) {
        status = AbiClrBp(MakeAbiAddr(bpHead->addr));
        NODE * pNode = bpHead;    
        bpHead = pNode->next;
        delete pNode;
    }
    return status;
}

/***************************************************************************
**
**  Name: EmuFreeRun
**
**  Function: Free run emulator
**
**  Return: the return code from ABI
**
***************************************************************************/
BOOL CEmuStepServer::EmuFreeRun()
{
    STATUS status;
    ADDR fromAddr, tillAddr;

    unsigned int uValue1, uValue2;

    fromAddr.addrType = 0;
    tillAddr.addrType = 0;

    status = ::AbiGo(0, fromAddr, tillAddr);

// Because sometimes, F/W will return echo before EP could(should) stopped
// at existing breakpoints. Not awaiting EP to stop but clearing breakpoints
// AT ONCE will cause error. So add checking status.
// Frank, 1/05/96

    while (1) {
        status = ::AbiGetCpuStatus(&uValue1);

        uValue2 = ((uValue1>>4) & 0x7);
        if ( 4 != uValue2) break;
        if ( 1 == ::TestKey(VK_ESCAPE) ) {
           // ::MessageBox( NULL, "Escape by user!",
           //        "Notice", MB_OK|MB_ICONASTERISK );
            m_fAbiRet = FALSE;
            return (FALSE);
        }
    }
    return TRUE;
}

/***************************************************************************
**
**  Name: EmuSeverStep
**
**  Function: Do step actions
**
**  Step [count] server for the Source window.
**  lCount:  1 <= lCount <= 0xFFFF - Count
**           0 - Continuously
**          -1 - Till Call
**          -2 - Till Ret
**          -3 - Into Call
**          -4 - Over Ret
**
***************************************************************************/
void EmuServerStep(long lCount)
{
    ASSERT( lCount >= -4 && lCount <= 0xFFFF );
                         
                         
    objEmuStep.m_fAsmMode = ::GetStepOption() == 0 ;  
    objEmuStep.m_fAbiRet = TRUE;
    //objEmuStep.m_fTooBigRoutine = FALSE;
    
    switch (lCount) {
        case  0: //step forever
            for (;;) {
                objEmuStep.StepOne();
                objEmuStep.UpdateAllWindows();
                
                if ( FALSE == objEmuStep.m_fAbiRet )
                    break;
                if ( 1 == ::TestKey(VK_ESCAPE) )
                    break;
            }
            break;

        case  1: //step one
            objEmuStep.StepOne();
            break; 
            
        case -1: //step till call
            objEmuStep.StepTillCall();
            break;

        case -2: //step till return
            objEmuStep.StepTillReturn();
            break;

        case -3: //step into call
            objEmuStep.StepIntoCall();
            break;

        case -4: // step return
            objEmuStep.StepOverReturn();
            break;

        default:
           for (long lLoop=0; lLoop < lCount; lLoop++) {
                objEmuStep.StepOne();
                objEmuStep.UpdateAllWindows();
                
                if ( FALSE == objEmuStep.m_fAbiRet ) 
                    break;
                if ( 1 == ::TestKey(VK_ESCAPE) ) {
                    break;
                }
           } 
           break;
    }
}



/***************************************************************************
**  Name: EmuServerStepOver
**
**  Function: execute "Step Over [count]" server for the Source window.
**
**  lCount:   1 <= lCount <= 0xFFFF - Count
**           0 - Continuously
**          -1 - Till Call
**          -2 - Till Ret
**          -3 - Into Call
**          -4 - Over Ret
**
***************************************************************************/
void EmuServerStepOver(long lCount)
{
    //ASSERT( lCount >= -4 && lCount <= 0xFFFF );

    objEmuStep.m_fAsmMode = ::GetStepOption() == 0 ;
    objEmuStep.m_fAbiRet = TRUE;
    //objEmuStep.m_fTooBigRoutine = FALSE;

    switch (lCount) {
        case  1: //step over
            objEmuStep.StepOver();
            break;
        case 0: // step over forever
            for (;;) {
                objEmuStep.StepOver();
                objEmuStep.UpdateAllWindows();
                if ( FALSE == objEmuStep.m_fAbiRet) 
                    break;
                if ( 1 == ::TestKey(VK_ESCAPE) )
                    break;
            }
            break;
        case -1:
            objEmuStep.StepTillCall();
            break;

        case -2:
            objEmuStep.StepTillReturn();
            break;

        case -3: // step over into call
            //cannot be explained
            objEmuStep.StepIntoCall();
            break;

        case -4: //step over return
            objEmuStep.StepOverReturn();
            break;

        default:
           for (long lLoop=0; lLoop < lCount; lLoop++) {
                objEmuStep.StepOver();
                objEmuStep.UpdateAllWindows();
                
                if ( FALSE == objEmuStep.m_fAbiRet) 
                    break;
                if ( 1 == ::TestKey(VK_ESCAPE) ) {
                    break;
                }
           } 
           break;
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Name:   StepCmd().
//
//  Description: The main control routine of Step command.
//
//  Input:  nArgc - Number of input parameters.
//          pszArgv - Input parameters.
//
//  Output: None.
//
//  Return: None.
//
/////////////////////////////////////////////////////////////////////////////
void StepCmd(int nArgc, char* pszArgv[])
{
    // Assertion of the input parameters.

    ASSERT( nArgc >= 1 && nArgc <= 6 );
    for ( int i = 0; i < nArgc; i++ ) {
        ASSERT( pszArgv[i] );
    }
    
    switch ( nArgc ) {
        case 1:
            // Step
            EmuServerStep(1);
            break;
        case 2:
            if ( 0 == stricmp(pszArgv[1], "OVER") ) {
                // Step Over
                EmuServerStepOver(1);
            }
            else if ( 0 == stricmp(pszArgv[1], "FOREVER") ) {
                //step forever
                EmuServerStep(0);
            }
            else {
                //step count
                DWORD dw;
                Str2Num(pszArgv[1], dw);
                EmuServerStep(dw);
            }
            break;
        case 3:
            if ( 0 == stricmp(pszArgv[1], "OVER") ) {
                if ( 0 == stricmp(pszArgv[2], "FOREVER") ) {
                    //step over forever
                    EmuServerStepOver(0);
                }
                else { 
                    // Step Over count
                    DWORD dw;
                    Str2Num(pszArgv[2], dw);
                    EmuServerStepOver(dw);
                }
            }
            else if ( 0 == stricmp(pszArgv[1], "TILL") ) {
                if ( 0 == stricmp(pszArgv[2], "RET") ) {
                    // Step Till Ret
                    EmuServerStep(-2);
                }
                else {
                    // Step Till Call
                    EmuServerStep(-1);
                }
            }
            else {
                // Step adr1 adr2
                ADDR fromAddr, tillAddr;
                
                StrToAddr(pszArgv[1], fromAddr);
                StrToAddr(pszArgv[2], tillAddr);

                

                objEmuStep.SetStepRange(fromAddr.addr, tillAddr.addr);
                objEmuStep.StepOne(OUT_USER_DEFINED);
            }
            break;
        case 4:
            if ( 0 == stricmp(pszArgv[2], "TILL") ) {
                if ( 0 == stricmp(pszArgv[3], "RET") ) {
                    // Step Over Till Return
                    EmuServerStepOver(-2);
                }
                else {
                    // Step Over Till Call
                    EmuServerStepOver(-1);
                }
            }
            else {
                // Step Over adr1 adr2
                ADDR fromAddr, tillAddr;
                
                StrToAddr(pszArgv[1], fromAddr);
                StrToAddr(pszArgv[2], tillAddr);

                objEmuStep.SetStepRange(fromAddr.addr, tillAddr.addr);
                objEmuStep.StepOver(OUT_USER_DEFINED);
            }
            break;
        case 5:
            if ( '@' == pszArgv[2][0] ) {
                int nRegID;
                DWORD dw;
                // Step Till @reg = value
                for ( int i = 0; i < ::nMaxReg; i++ ) {
                    if ( 0 == stricmp(pszArgv[2]+1, ::RegName[i]) ) {
                        nRegID = i;
                        break;
                    }
                }
                ASSERT( nRegID >= 0 && nRegID < nMaxReg );
                Str2Num(pszArgv[4], dw);
                StepTillDestReg(nRegID, (WORD) dw);
            }
            else {
                // Step Till adr = value
                ADDR addr;
                DWORD dw;
                StrToAddr(pszArgv[2], addr);
                Str2Num(pszArgv[4], dw);
                StepTillDestAddr(addr, (WORD) dw);
            }

            break;
        case 6:
            if ( '@' == pszArgv[3][0] ) {
                // Step Over Till @reg = value
                int nRegID;
                DWORD dw;
                // Step Till @reg = value
                for ( int i = 0; i < ::nMaxReg; i++ ) {
                    if ( 0 == stricmp(pszArgv[2]+1, ::RegName[i]) ) {
                        nRegID = i;
                        break;
                    }
                }
                ASSERT( nRegID >= 0 && nRegID < nMaxReg );
                Str2Num(pszArgv[4], dw);
                StepTillDestReg(nRegID, (WORD) dw);
            }
            else {
                // Step Over Till adr = value
                ADDR addr; 
                DWORD dw;
                StrToAddr(pszArgv[3], addr);
                Str2Num(pszArgv[5], dw);
                StepTillDestAddr(addr, (WORD) dw);
            }
            break;
        default:
            ASSERT( FALSE );
            break;
    }

}

////////////////////////////////////////////////////////////////////////////
CEmuStepServer::CEmuStepServer()
{
    indexHead = NULL;
    bpHead = NULL;               
    m_fAbiRet = TRUE;
    m_fTooBigRoutine = FALSE;
}

//call for such cmd: 
//	1. Step forerver;
//	2. step;
//	3. step count;
//	4. step adr1 adr2(nType==OUT_USER_DEFINED)
void CEmuStepServer::StepOne(int nType)
{                           
    if ( m_fAsmMode == TRUE) { // is MIXED mode or ASM mode
        ::AbiStepOne();
        return;
    }

    //is C source mode
    if ( OUT_USER_DEFINED == nType )
        m_stratagem = AnalyseCmdSet ( 4 );
    else
        m_stratagem = AnalyseCmdSet ( 0 );

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
        JustGoOutside();
    }

    // No.1 instruction is just to call a sub routine
    else if ( JUST_STEP_ONE == m_stratagem ) {
        ::AbiStepOne();
    }

    else {
        TrytoGoOutside();
    }

    delete m_uchMasnCode;
}


void CEmuStepServer::StepOver(int nType)
{                            
    if ( m_fAsmMode == TRUE) {// is MIXED or ASM mode
        AsmStepOver();
        return;
    }

    //is C source mode
    if ( OUT_USER_DEFINED ==  nType )
        m_stratagem = AnalyseCmdSet ( 5 );
    else
        m_stratagem = AnalyseCmdSet ( 1 );

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
        JustGoOutside();
    }

    else if ( JUST_STEP_ONE == m_stratagem ) {
        int nSpecial;
        nSpecial = IsSpecialMasnCode(m_uchMasnCode[0]);
        if ( nSpecial == 1 || nSpecial == 2 ) {
            EmuSetTempBp(m_uTillAddr + 1);
            m_fAbiRet = EmuFreeRun();
            EmuClrTempBp();
        }
        else {
            ::AbiStepOne();
        }    
    }

    else {
        TrytoGoOutside();
    }
    delete m_uchMasnCode;
}

void CEmuStepServer::StepOverReturn()
{   
    if ( m_fAsmMode == TRUE) {// is MIXED or ASM mode
        AsmStepOut(TRUE);
        return;
    }
    
    m_fTooBigRoutine = FALSE;
    //is C source mode
    m_stratagem = AnalyseCmdSet ( 2, OUT_FUNCTION );

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
    	if ( FALSE == m_fTooBigRoutine )
        	::MessageBox(NULL, "Cannot get return address!", "Error",
                     MB_OK|MB_ICONASTERISK);
        else 
        	::MessageBox(NULL, "Sorry, 'Go Over Return' is not fit here!",
        			 "Notice", MB_OK|MB_ICONASTERISK);
             
    }

    else if ( JUST_STEP_ONE == m_stratagem ) {
        ::AbiStepOne();
    }

    else {
        TrytoGoOutside(OUT_FUNCTION);
        ::AbiStepOne();
    }
    delete m_uchMasnCode;
}


BOOL CEmuStepServer::JustGoOutside()
{ 
    EmuSetTempBp(m_uTillAddr+1);
    m_fAbiRet = EmuFreeRun();
    EmuClrTempBp();

    return m_fAbiRet;
}
  
                                   
BOOL CEmuStepServer::TrytoGoOutside(int nType)
{
    //because of special lib_func's (not ret but Jmp @a+dptr)
    PADDR uCurPC  = m_uStart;
  //  BOOL fSelfOut = TRUE;
 
    if ( OUT_STATEMENT == nType ) 
        EmuSetTempBp(m_uTillAddr + 1);

    NODE * pNode  = indexHead;
    while ( pNode ) {
        EmuSetTempBp(pNode->addr);
        pNode = pNode->next;
    }
    
    m_fAbiRet = EmuFreeRun();
             
    EmuClrTempBp();

    return m_fAbiRet;
}

void CEmuStepServer::UpdateAllWindows()
{
    // Repaint all the windows.
    ::OnEmulation();

    if ( ::isSourceOn ) {
        ::pSourceWnd->UpdateWindow();
    }
    if ( ::isVariableOn ) {
        ::pVariableWnd->UpdateWindow();
    }
    if ( ::isStackOn ) {
        ::pStackWnd->UpdateWindow();
    }
    if ( ::isCpuOn ) {
        ::pCpuWnd->UpdateWindow();
    }
    if ( ::isTraceOn ) {
        ::pTraceWnd->UpdateWindow();
    }
    if ( ::isPeriOn ) {
        ::pPeriWnd->UpdateWindow();
    }
    if ( ::isBMemOn ) {
        ::pBMemWnd->UpdateWindow();
    }
    for ( int i = 0; i <= 2; i++ ) {
        if ( ::isMemOn[i] ) {
            ::pMemWnd[i]->UpdateWindow();
        }
    }
}


void CEmuStepServer::AsmStepOver()
{
    int nSpecial;     
    
    ::AbiGetOneReg(0, (UINT*) &m_uStart);    
    
    m_uchMasnCode = new unsigned char [4];
    ::AbiGetMemN(MakeAbiAddr(m_uStart),
                       MakeAbiAddr(m_uStart+3),
                       (char *) m_uchMasnCode);
    nSpecial = IsSpecialMasnCode(m_uchMasnCode[0]);
    if ( nSpecial == 1 || nSpecial == 2 ) 
    {
        if ( nSpecial == 1) { //acall
            EmuSetTempBp(m_uStart + 2);
        }
        else { //lcall
            EmuSetTempBp(m_uStart + 3);
        }
        EmuSetTempBp(m_uTillAddr + 1);

        m_fAbiRet = EmuFreeRun();
        EmuClrTempBp();
    }
    else 
        ::AbiStepOne();

    delete m_uchMasnCode;

}

void CEmuStepServer::AsmStepOut(BOOL bOverRet, BOOL /*bOver*/)
{  
    int nSpecial;     
    while (1) { 
        if ( 1 == ::TestKey(VK_ESCAPE) ) 
            break;
                    
        ::AbiGetOneReg(0, (UINT*) &m_uStart);    
        
        m_uchMasnCode = new unsigned char [4];
        ::AbiGetMemN(MakeAbiAddr(m_uStart),
                           MakeAbiAddr(m_uStart+3),
                           (char *) m_uchMasnCode);
    
        if ( (nSpecial = IsSpecialMasnCode(m_uchMasnCode[0])) == 1 || 
              nSpecial == 2 ) 
        {
            if ( nSpecial == 1) { //acall
                EmuSetTempBp(m_uStart + 2);
            }
            else { //lcall
                EmuSetTempBp(m_uStart + 3);
            }
            EmuSetTempBp(m_uTillAddr + 1);
    
            m_fAbiRet = EmuFreeRun();
            EmuClrTempBp();
        }           
        else if ( 4 == nSpecial ) {		//ret or reti
            if(bOverRet)
            	::AbiStepOne();
            break;
        }
        else 
            ::AbiStepOne();
    }
    delete m_uchMasnCode;
}

void CEmuStepServer::AsmStepInto(BOOL bIntoCall, BOOL /*bOver*/)
{
    int nSpecial;     
    while (1) { 
        if ( 1 == ::TestKey(VK_ESCAPE) ) 
            break;
                    
        ::AbiGetOneReg(0, (UINT*) &m_uStart);    
        
        m_uchMasnCode = new unsigned char [4];
        ::AbiGetMemN(MakeAbiAddr(m_uStart),
                           MakeAbiAddr(m_uStart+3),
                           (char *) m_uchMasnCode);
    
        if ( (nSpecial = IsSpecialMasnCode(m_uchMasnCode[0])) == 1 || 
              nSpecial == 2 ) //call
        {
            if(bIntoCall)
           		::AbiStepOne();
            break;
        }           
        else 
            ::AbiStepOne();
    }
    delete m_uchMasnCode;
}

void CEmuStepServer::StepTillCall()
{
    if ( m_fAsmMode == TRUE) { // is MIXED mode or ASM mode
        AsmStepInto(FALSE);
        return;
    }
  
    m_fTooBigRoutine = FALSE;
  
    //is C source mode
    m_stratagem = AnalyseCmdSet ( 7, OUT_FUNCTION ); // 7 for till call

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
    	if ( FALSE == m_fTooBigRoutine )
        	::MessageBox(NULL, "Cannot get a sub routine!", "Error",
                     MB_OK|MB_ICONASTERISK);         
        else
        	::MessageBox(NULL, "Sorry, 'Go Till Call' is not fit here!", 
        			"Notice", MB_OK|MB_ICONASTERISK);
    }

    // No.1 instruction is just to call a sub routine
    else if ( JUST_STEP_ONE == m_stratagem ) {
        ::MessageBox(NULL, "You now are at the destination!", "Notice",
                     MB_OK|MB_ICONASTERISK);
    }

    else {
        TrytoGoOutside(OUT_FUNCTION);
    }

    delete m_uchMasnCode;
}

void CEmuStepServer::StepTillReturn()
{
    if ( m_fAsmMode == TRUE) {// is MIXED or ASM mode
        AsmStepOut(FALSE);
        return;
    }

    m_fTooBigRoutine = FALSE;
    //is C source mode
    m_stratagem = AnalyseCmdSet ( 6, OUT_FUNCTION );

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
    	if ( FALSE == m_fTooBigRoutine )   
        	::MessageBox(NULL, "Cannot get return address!", "Error",
                     MB_OK|MB_ICONASTERISK);
		else 	       
          	::MessageBox(NULL, "Sorry, 'Go Till Return' is not fit here!", 
        			"Notice", MB_OK|MB_ICONASTERISK);

    }

    else if ( JUST_STEP_ONE == m_stratagem ) {
        ::AbiStepOne();
    }

    else {
        TrytoGoOutside(OUT_FUNCTION);
    }
    delete m_uchMasnCode;
}

void CEmuStepServer::StepIntoCall()
{
    if ( m_fAsmMode == TRUE) { // is MIXED mode or ASM mode
        AsmStepInto(TRUE);
        return;
    }

    m_fTooBigRoutine = FALSE;
    //is C source mode
    m_stratagem = AnalyseCmdSet ( 3, OUT_FUNCTION ); // 3 for call

    if ( JUST_DO_NOTHING == m_stratagem )
        return;

    if ( JUST_GO_OUTSIDE == m_stratagem ) { //normal commands
        if ( FALSE == m_fTooBigRoutine )
        	::MessageBox(NULL, "Cannot get a sub routine!", "Error",
                     MB_OK|MB_ICONASTERISK);
        else                                
        	::MessageBox(NULL, "Sorry, 'Go Into Call' is not fit here!", 
        			"Notice", MB_OK|MB_ICONASTERISK);
    }

    // No.1 instruction is just to call a sub routine
    else if ( JUST_STEP_ONE == m_stratagem ) {
        ::AbiStepOne();
    }

    else {
        TrytoGoOutside(OUT_FUNCTION);
    }

    delete m_uchMasnCode;
}

void StepTillDestReg(int nRegId, WORD wRegValue)
{
    WORD wValue;

    while (1) {
        if ( 1 == ::TestKey(VK_ESCAPE) ) {
            ::ShowLine("Abort by user!");
            break;
        }
        AbiGetOneReg(nRegId, (UINT *) &wValue);
        if ( wValue ==  wRegValue) break;

        AbiStepOne();
    }

}

void StepTillDestAddr(ADDR addr, WORD wAddrValue)
{
    unsigned char uch[2];
    
    
    while (1) {
        if ( 1 == ::TestKey(VK_ESCAPE) ) {
            ::ShowLine("Abort by user!");
            break;
        }
        AbiGetMemN(addr, addr, (char *) uch);
        if ( uch[0] ==  (unsigned char ) wAddrValue) break;

        AbiStepOne();
    }

}

void CEmuStepServer::SetStepRange(PADDR paddrFrom, PADDR paddrTill)
{
    m_uFromAddr = paddrFrom;
    m_uTillAddr = paddrTill;
}


