Page 1 of 2

Timing of emulator base on win32

Posted: Tue Aug 26, 2003 3:26 pm
by skyler
I'd want to build an Emulator base on win32,I refer some code and I can achieve instruction of 6502,but how to make the emulator run speed is the same of actual speed(about 4Mhz)?

Posted: Tue Aug 26, 2003 6:07 pm
by 8BIT
Skyler,

In my simulator, I poll the system timer, which increments every 1ms.

What I did is to let the simulator execute 1ms worth of instructions (4000 cycles @ 4MHz) then wait for that ms to end. Then, I do another 1ms worth of instructions and wait. For most programs, this is acceptable. Sound generation based on timing loops would not be accurate since the loops would not be equally timed.

In my next release, I am attempting to time each instruction more accurately by using timing loops, cycle counters, and feedback to equally space each instruction within the 1ms window.

You can download my simulator and the C source code from my web site.

http://65c02.tripod.com/

Good luck!

Daryl

Posted: Wed Aug 27, 2003 3:33 pm
by schidester
For sound, you'd probably want to buffer up 4000 cycles worth of sound and pass that off to the Windows sound API as a 1ms buffer to play in the background, then work on the next 4000 cycles in another buffer. But you probably don't want to bit-bang sound from a simulator. As a matter of fact, (I haven't looked at this in a long time, and not to deeply at that), you could use the notification that Windows gives you that it's done with the buffer and ready for the next as your 1ms timer, instead of polling.

Scott

Posted: Thu Aug 28, 2003 1:43 am
by skyler
8Bit,

Thanks for your help,I know how to do now,run 4000 cycly then wait for each ms to end is and good way for me.Because on my project,there are no sound generation^o^
And why I can't visit your web sits?Can you send my your code to my MailBox directly? [skyler@cnnb.net]
Thank you.

Skyler.

Posted: Fri Aug 29, 2003 2:19 pm
by schidester
Daryl & Skyler,

You might still want to consider doing a blocking wait in your simulators instead of polling a timer. Daryl, I checked your simulator's CPU usage, and it's at 99%. A quick way to do this would be to call the Sleep function for the time you need to wait.

Scott

Posted: Fri Aug 29, 2003 4:53 pm
by 8BIT
Scott,

First, I'm no expert on Windows programming. I read up on the sleep function and found that it only has millisecond resolution. I'm counting cycles within a 1ms window. If the CPU takes .38ms to process the instructions, I cannot tell it to sleep for .62ms.

I can only sleep for whole ms periods. Do you have any suggestions?

Daryl

Posted: Fri Aug 29, 2003 6:24 pm
by schidester
Daryl,

Hmm.... I'm somewhat novice myself on the Win32 stuff. And for "no expert," your simulator sure seems cool. OK, enough flattery. Some suggestions:

1. If it was Linux (or UNIX), I'd say use pthread_cond_timedwait() which uses an absolute time; then, no matter how long your instructions took to execute, you'd be woken up on the next millisecond. For example, if your run your simulation at Aug 29th, 2003, at 1:20:15.0100, then execute 4000 cycles and sleep until Aug 29th, 2003, at 1:20:15.0101. There should be an equivalent "wait until absolute time" function in Win32, but I couldn't find it.

2. You can set up a separate thread using CreateThread, which waited on 1ms intervals and incremented a semaphore. Your 6502 thread would simply execute 4000 cycles and then wait on the semaphore. This may still be off because of the time taken between 1ms waits to post the semaphore.

3. You can increase the period of your simulation loop, for example, execute for an equivalent of 100 ms. Then, if it takes 38.5 ms to execute 400,000 cycles, you'll wait for the remaining 62 ms with an insignificant amount of real-time error. Of course, you may get some flicker in your simulation at 100ms, but if it's just a simulation for testing, it should be fine. Or trade off timing accuracy for smoothness.

There are a few more ways to do it, but the absolute time method (#1) is best (if Win32 provides it). If you can wake up on 1ms boundaries and execute your 4000 cycles, and then sleep until the next 1ms boundary, you'll be dead-on, and your program won't hog the CPU.

Scott

Posted: Wed Sep 17, 2003 7:21 am
by skyler
If it can not finish 4000cycle per second.Such as PC speed is not so fast or current status is too busy.What can I do next ms?
I use MMTimer,It can produce MS event.and I run 4000cycle in it's callback function.

Posted: Wed Sep 17, 2003 2:27 pm
by schidester
If you are waiting for an absolute time, such as "wait until 9:22 am, 50.0001 seconds" and, after running 4000 cycles you find that it's already 50.0004 seconds, you simply run your next 4000 cycles--your wait function should return immediately. The simulation will run slow, but it will also run no faster than it would in real time.

If your PC is too slow to run a 6502 simulation at 4MHz, then it's probably too slow to run Windows. If your simulation is honestly too slow for a Windows box, then you should probably take some time to optimize it. If you're using DOS, then just poll anyway--nothing else but your simulation will be running.

I couldn't find much help on MM_TIMER; I'm using an older VisualStudio. Are you using MFC? Are you programming for NT/2000 or 95/98? Are you familiar with multithreaded programming?

Scott

Posted: Wed Sep 17, 2003 2:28 pm
by 8BIT
Skyler,

You could set a variable called target_cycles.

At the end of a ms period, if there are any unexecuted cycles, then set target_cycles = unexecuted cycles. If all the cycles were executed, set it to zero.

Now, before entering the next ms period, ADD 4000 to target_cycles and use that value as the number of cycles to execute.

Daryl

Posted: Wed Sep 17, 2003 6:26 pm
by skyler
Daryl,

I know how to do now.thank you.

skyler

+++++++++++++++++++++++++++++++++++++++++++++++

Scott,

I think Daryl's way is a good way to resolve my problem.
Infact,the below code just occupy CPU usage just 2-4%.I think it will work fine in the future.
I'll set a target_cycles,once the computer is too busy to finish 4000 cycle in ms,this will make it correct.
I'm using SDK and I want my simulation can run in NT,98,2K and XP.I'm an beginer of C++,I don't know about MFC.:(
Below is the code I use in my project now.

Thank's for your help.

skyler

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

Code: Select all

CMMTimers::CMMTimers()
{
TIMECAPS	tc;
if (!Intilizatize)
	{
		if (TIMERR_NOERROR == ::timeGetDevCaps(&tc,sizeof(TIMECAPS)))
		{
			timerRes = min(max(tc.wPeriodMin,1),tc.wPeriodMax);
			timeBeginPeriod(timerRes);
		}
	}
}
CMMTimers::~CMMTimers()
{
	stopTimer();
	if (0 != timerRes)
	{
		timeEndPeriod(timerRes);
		timerRes = 0;
	}
}
void CALLBACK internalTimerProc(UINT id,UINT msg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
	static char str[40];
	sprintf(str,"Cycle:%08d",MMTimer.Counter);
	TextOut(Debugger.hdcMSG,0,0,str,strlen(str));
	MMTimer.Counter++;
	if (CPU.RUN) CPU.EXE(4000);
}
bool CMMTimers::startTimer(void)
{
	bool	res = false;
	MMRESULT	RETURN;
	if (!Runing)
	{
		Counter=0;
		result = timeSetEvent(
			1,
			timerRes,
			internalTimerProc,
			(DWORD)this,
			TIME_PERIODIC
			);
		if (NULL != RETURN)
		{
			timerId = (UINT)RETURN;
			Runing=true;
			res = true;
		}
		return res;
	}
	return false;
}
bool CMMTimers::stopTimer()
{
	MMRESULT	RETURN;
	result = timeKillEvent(timerId);
	if (TIMERR_NOERROR == RETURN)
		timerId = 0;
	Runing = RETURN;
	return TIMERR_NOERROR == RETURN;
}
***********************************************************

Posted: Wed Sep 17, 2003 9:47 pm
by schidester
Skyler,

Yeah, that looks like it'll work. The TIMER_PERIODIC is what really saves you. At 2% to 4%, I wouldn't worry about not getting the 4000 cycles done in time at all, but if you don't, you should get a callback for each millisecond missed.

After you start your timer you probably return back to Windows's message loop, which blocks and gives up the CPU until the timeouts occur.

Scott.

Posted: Thu Sep 18, 2003 2:08 am
by skyler
Scott,

Because I have not simulate the LCD controler,after do this,I think it will become slowly.
If it can finish each ms task,it will exit callback and return to windows's message loop.
If it can not do this,Can i insert the code below to translate windows's message before start next ms task?If I not to do this,windows will have no chance to translate message.I can't prove the code will work fine.

thank you.

skyler

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

Code: Select all

void CALLBACK internalTimerProc(UINT id,UINT msg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
        static char str[40];
        static bool Intime;
        do
        {
                T1=GetCurrentTime();
                sprintf(str,"Cycle:%08d",MMTimer.Counter);
                TextOut(Debugger.hdcMSG,0,0,str,strlen(str));
                MMTimer.Counter++;
                if (!CPU.RUN) return;
                CPU.EXE(4000);
                T2=GetCurrentTime();
                if ((T2-T1)>=1ms) 
                {       
                        Intime=true;
                        DoEvent();
                }
                else 
Intime=false;
        }
        while (Intime);
}

void DoEvent()
{	
	MSG msg;
        BOOL bRet; 
	bRet = GetMessage( &msg, NULL, 0, 0 ); 
	if (bRet != -1)
	{
		TranslateMessage(&msg); 
		DispatchMessage(&msg); 
	}
}
******************************************************

Posted: Thu Sep 18, 2003 2:20 pm
by schidester
Skyler,

Again, I'm only a novice at Windows programming. It seems to me that whether or not you finish your 4000 cycles in 1ms, you want to return to your message loop. Then if you couldn't complete your simulation in 1ms, a timeout message would be waiting for you immediately, wouldn't it?
Quote:
If I not to do this,windows will have no chance to translate message.
Are you sure? I'm not sure how it works since I've only used a little MFC, but I'd think that any messages posted to your application would simply be waiting in a queue when you get back to the message loop.

Scott

Posted: Thu Sep 18, 2003 7:26 pm
by kc5tja
Quote:
CMMTimers::CMMTimers()
{
TIMECAPS tc;
if (!Intilizatize)
{
if (TIMERR_NOERROR == ::timeGetDevCaps(&tc,sizeof(TIMECAPS)))
{
timerRes = min(max(tc.wPeriodMin,1),tc.wPeriodMax);
timeBeginPeriod(timerRes);
}
}
}
Folks, when posting code, can you please wrap it inside a CODE block? That's what it's there for, and it preserves spacing and indentation, which for C++, is *critical* for understanding the code.

This is what the above would look like if posted, verbatim, inside a code block:

Code: Select all

CMMTimers::CMMTimers()
{
TIMECAPS	tc;
if (!Intilizatize)
	{
		if (TIMERR_NOERROR == ::timeGetDevCaps(&tc,sizeof(TIMECAPS)))
		{
			timerRes = min(max(tc.wPeriodMin,1),tc.wPeriodMax);
			timeBeginPeriod(timerRes);
		}
	}
}
Believe me, everyone will truely appreciate it. I know I sure do.