Try my ProEmulator. It's powerful yet free. You can even write a CPU for it (in the form of a DLL, using asm or C, more like a plug-in) . I use win32 assembly to write the 8051 CPU core. It's under busy development. So try it and give me some suggestions.

http://member.netease.com/~huangyc/chip/
Posted on 2003-11-11 09:08:29 by optimus
This is a sample 8-bit CPU core written in C, which can be implemented in ProEmulator.

#include <windows.h>


#define CPU_ID 1500
#define CPU_Name "Sample CPU Core"
#define PC_After_Reset 0
#define CodeMemSize 256 /* size of code memory in bytes */
#define DataMemSize 256 /* size of data memory in bytes */
#define SFR_Count 5 /* number of Special Function Registers */
#define REG_Count 4 /* number of registers */
#define REG_Bank 1 /* number of register banks */
#define PSW_Bits 8 /* number of PSW bits */
#define Code_Bits 8 /* 8 bit, 16 bit etc. */
#define Data_Bits 8

#define REG_Names "B0"
#define SFR_Names "PC PSW ACC SP B "
#define PSW_Names "CY AC F1 S1 S0 OV F0 P "

#define API_Init 0
#define API_StepRun 1
#define API_RunRealtime 2
#define API_StartRun 3
#define API_StopRun 4
#define API_GetMem 5
#define API_SetMem 6
#define API_DumpMemory 7
#define API_FlushMemory 8
#define API_GetBit 9
#define API_SetBit 10
#define API_SetMapping 11
#define API_GetReg 12
#define API_SetReg 13
#define API_GetCPUInfo 14
#define API_SetHiLiteWord 15
#define API_GetSFR 16
#define API_SetSFR 17
#define API_ResetSystem 18

#define SFR_PC 0
#define SFR_PSW 1
#define SFR_ACC 2
#define SFR_SP 3
#define SFR_B 4

#define PC SFR[SFR_PC]
#define ACC SFR[SFR_ACC]
#define SP SFR[SFR_SP]
#define PSW SFR[SFR_PSW]
#define NEXTCODE CodeMem[PC++]

#define REM_SETHILITEWORDS 0x400+1000

typedef struct MEMOP_STRUCT{
long MemType;
long lpData;
long lpHexData;
long StartAddr;
long NumOfBytes;
long lpCallBack;
}MEMOP;

BYTE CodeMem[CodeMemSize-1]; /* Code memory space */
BYTE DataMem[DataMemSize-1]; /* Data memory space */
BYTE SFR[SFR_Count-1]; /* SFR space */
BYTE REG[REG_Count-1]; /* Register space */

BOOL WINAPI __declspec(dllexport) LibMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

long StartRun() {return(0);}
long StopRun() {return(0);}
long RunRealtime() {return(0);}
long GetBit() {return(0);}
long SetBit() {return(0);}


long ResetSystem ()
{
PC=PC_After_Reset; /* Initializing PC */
ACC=0;
SP=7; /* Initializing stack pointer */
PSW=0;
return(1);
}

long Init(long CPUFreq) /* CPUFreq is the detected host CPU frequency */
{
return(ResetSystem()); /* Nothing really to initialize for this simple CPU */
}

long GetCPUInfo (long InfoType,DWORD lpBuf)
{
switch (InfoType) {
case 0:
return(CPU_ID);
case 1:
return(PC_After_Reset);
case 3:
strcpy((char *)lpBuf,CPU_Name);
return(strlen(CPU_Name));
case 4:
strcpy((char *)lpBuf,REG_Names);
return(REG_Bank);
case 5:
strcpy((char *)lpBuf,SFR_Names);
return(SFR_Count);
case 6:
strcpy((char *)lpBuf,PSW_Names);
return(PSW_Bits);
case 7:
return(Code_Bits);
case 8:
return(Data_Bits);
case 10:
return(CodeMemSize);
case 11:
return(DataMemSize);
case 15:
return(SFR_Count);
case 16:
return(REG_Count);
case 17:
return(REG_Bank);
case 20:
return((long)CodeMem);
case 21:
return((long)DataMem);
case 22:
return(0);
}
return(-1);
}

long GetReg (long RegBank, long RegNo)
{
if (RegNo<REG_Count) return(REG[RegNo]);
return(-1);
}

long SetReg (long RegBank, long RegNo, long RegVal)
{
if (RegNo<REG_Count) return(REG[RegNo]=(BYTE)RegVal);
return(-1);
}

long GetSFR (long SFRNo)
{
if (SFRNo<SFR_Count) return(SFR[SFRNo]);
return(0);
}

long SetSFR (long SFRNo,BYTE SFRVal)
{
if (SFRNo<SFR_Count) return(SFR[SFRNo]=SFRVal);
return(-1);
}

long GetMem (long MemType,long MemAddr)
{
switch (MemType){
case 0: /* addressing code memory */
if (MemAddr<CodeMemSize) return(CodeMem[MemAddr]);
break;
case 1: /* addressing data memory */
if (MemAddr<DataMemSize) return(DataMem[MemAddr]);
break;
}
return(-1);
}

long SetMem (long MemType,long MemAddr,BYTE MemData)
{
switch (MemType){
case 0: /* addressing code memory */
if (MemAddr<CodeMemSize) return(CodeMem[MemAddr]=MemData);
break;
case 1: /* addressing data memory */
if (MemAddr<DataMemSize) return(DataMem[MemAddr]=MemData);
break;
}
return(-1);
}

long DumpMemory (MEMOP *lpMemOp)
{
switch (lpMemOp->MemType){
case 0: /* addressing code memory */
RtlMoveMemory((void*)lpMemOp->lpData,CodeMem,lpMemOp->NumOfBytes);
break;
case 1: /* addressing data memory */
RtlMoveMemory((void*)lpMemOp->lpData,DataMem,lpMemOp->NumOfBytes);
break;
default:
lpMemOp->NumOfBytes=-1; /* no other memory type */
break;
}
return(lpMemOp->NumOfBytes);
}

long FlushMemory (MEMOP *lpMemOp)
{
switch (lpMemOp->MemType){
case 0: /* addressing code memory */
RtlMoveMemory(CodeMem,(void*)lpMemOp->lpData,lpMemOp->NumOfBytes);
break;
case 1: /* addressing data memory */
RtlMoveMemory(DataMem,(void*)lpMemOp->lpData,lpMemOp->NumOfBytes);
break;
default:
lpMemOp->NumOfBytes=-1; /* no other memory type */
break;
}
return(lpMemOp->NumOfBytes);
}

long SetHiliteWords(long hRed)
{
/* This routine sets the highlighted keywords in code editor */
char *szRegs="PC ACC SP PSW B";
char *szInst="MOV ADD INC NOP DEC SUB JMP";
char *szAsm="";

SendMessage((HWND)hRed,REM_SETHILITEWORDS,0x0A00000,(long)szRegs);
SendMessage((HWND)hRed,REM_SETHILITEWORDS,0x40A000,(long)szInst);
SendMessage((HWND)hRed,REM_SETHILITEWORDS,0x4040A0,(long)szAsm);
return(1);
}

void OP_NOP() {} /* NOP */
void OP_MOV_ACC_Data() {ACC=NEXTCODE;} /* MOV ACC,Data */
void OP_MOV_ACC_Mem() {ACC=DataMem[NEXTCODE];} /* MOV ACC,Mem */
void OP_MOV_Mem_ACC() {DataMem[NEXTCODE]=ACC;} /* MOV Mem,ACC */
void OP_INC_ACC() {ACC++;} /* INC ACC */
void OP_DEC_ACC() {ACC--;} /* DEC ACC */
void OP_INC_Mem() {DataMem[NEXTCODE]++;} /* INC Mem */
void OP_DEC_Mem() {DataMem[NEXTCODE]--;} /* DEC Mem */
void OP_ADD_A_Data() {ACC+=NEXTCODE;} /* ADD A,Data */
void OP_ADD_A_Mem() {ACC+=DataMem[NEXTCODE];} /* ADD A,Mem */
void OP_SUB_A_Data() {ACC-=NEXTCODE;} /* SUB A,Data */
void OP_SUB_A_Mem() {ACC-=DataMem[NEXTCODE];} /* SUB A,Mem */
void OP_JMP_PC() {PC=NEXTCODE;} /* JMP PC */
void OP_PUSH_Mem() {DataMem[++SP]=DataMem[NEXTCODE];} /* PUSH Mem */
void OP_POP_Mem() {DataMem[NEXTCODE]=DataMem[SP--];} /* POP Mem */


void* lpOpProc[15]={
OP_NOP,OP_MOV_ACC_Data,OP_MOV_ACC_Mem,OP_MOV_Mem_ACC,OP_INC_ACC,
OP_DEC_ACC,OP_INC_Mem,OP_DEC_Mem,OP_ADD_A_Data,OP_ADD_A_Mem,
OP_SUB_A_Data,OP_SUB_A_Mem,OP_JMP_PC,OP_PUSH_Mem,OP_POP_Mem
};

long RunOP(long (*f)()){
return (*f)();
}


long StepRun ()
{
/* This routine emulates the CPU behavior */
BYTE OpCode=NEXTCODE;
if (OpCode<15) RunOP(lpOpProc[OpCode]);
return(PC);
}

__declspec(dllexport) long CallAPI(DWORD API,DWORD arg1,DWORD arg2,DWORD arg3)
{
/* This is the only exported function */
switch(API)
{
case API_Init:
return(Init(arg1));
case API_StepRun:
return(StepRun());
case API_StartRun:
break;
case API_StopRun:
break;
case API_GetMem:
return(GetMem(arg1,arg2));
case API_SetMem:
return(SetMem(arg1,arg2,arg3));
case API_GetReg:
return(GetReg(arg1,arg2));
case API_SetReg:
return(SetReg(arg1,arg2,arg3));
case API_GetCPUInfo:
return(GetCPUInfo(arg1,arg2));
case API_SetHiLiteWord:
return(SetHiliteWords(arg1));
case API_GetSFR:
return(GetSFR(arg1));
case API_SetSFR:
return(SetSFR(arg1,arg2));
case API_ResetSystem:
return(ResetSystem());
case API_SetBit:
return(SetBit());
case API_GetBit:
return(GetBit());
case API_DumpMemory:
return(DumpMemory((MEMOP *)arg1));
case API_FlushMemory:
return(FlushMemory((MEMOP *)arg1));
}
return(-1);
}


The following is the virtual CPU running code in ProEmulator. As u can see the result is correct.

Posted on 2003-11-12 22:51:40 by optimus
Hi Optimus

Well most of us here use the x86 IS, so if it were possible could you write one for x86 with 32-bit support. If you do, I would try to use it in our wiki book as a teaching aid. Thanks.

Regards,
Art
Posted on 2003-11-16 05:10:09 by art_sands

Try my ProEmulator. It's powerful yet free. You can even write a CPU for it (in the form of a DLL, using asm or C, more like a plug-in) . I use win32 assembly to write the 8051 CPU core. It's under busy development. So try it and give me some suggestions.

http://member.netease.com/~huangyc/chip/


Nice work optimus, I will play with it :D
Posted on 2003-11-16 17:04:51 by x86asm
Well I'd like to support x86 IS. But that won't be called emulation running x86 code on a x86, right? Emulating x86 on x86 requires another algorithm for better performance, like what VirtualPC or VMWare do. Usually RISC CPUs are easier to emulate because of their fixed instruction length (e.g. 32 bits). I've been think of emulating x86 16-bit mode, cause we learn that in our college, and that might be useful for debugging 16-bit code in 32-bit os which everybody uses. If anybody familiar with x86 opcodes can write a x86 emulation module, I would very glad.

ps: currently ProEmulator only handles 8-bit CPU well. I will improve it soon so that 32-bit CPU can be well emulated.


Hi Optimus

Well most of us here use the x86 IS, so if it were possible could you write one for x86 with 32-bit support. If you do, I would try to use it in our wiki book as a teaching aid. Thanks.

Regards,
Art
Posted on 2003-11-16 20:31:45 by optimus
I've updated the CPU code above and placed a screenshot of the CPU written by that code emulated by ProEmulator.
Posted on 2003-11-16 20:35:14 by optimus

Well I'd like to support x86 IS. But that won't be called emulation running x86 code on a x86, right? Emulating x86 on x86 requires another algorithm for better performance, like what VirtualPC or VMWare do. Usually RISC CPUs are easier to emulate because of their fixed instruction length (e.g. 32 bits). I've been think of emulating x86 16-bit mode, cause we learn that in our college, and that might be useful for debugging 16-bit code in 32-bit os which everybody uses. If anybody familiar with x86 opcodes can write a x86 emulation module, I would very glad.

ps: currently ProEmulator only handles 8-bit CPU well. I will improve it soon so that 32-bit CPU can be well emulated.



Actually, it's not that bad. Just turn on the trace bit and let the CPU decode the instruction lengths for you. Then all you've got to do is trap all the addresses and respond accordingly.
Not trivial, but not as difficult as you're thinking, either.
Cheers,
Randy Hyde
Posted on 2003-11-18 11:16:50 by rhyde
does that work in protected os like windows?
Posted on 2003-11-18 19:29:51 by optimus
A new screenshot of my emulator. Implemented with KetilO's three asm custom controls.
btw: the code at top has been updated also.


Posted on 2003-11-24 02:42:46 by optimus