6

I'm writing a MOS 6502 processor emulator as part of a larger project I've undertaken in my spare time. The emulator is written in Java, and before you say it, I know its not going to be as efficient and optimized as if it was written in c or assembly, but the goal is to make it run on various platforms and its pulling 2.5MHZ on a 1GHZ processor which is pretty good for an interpreted emulator. My problem is quite to the contrary, I need to limit the number of cycles to 1MHZ. Ive looked around but not seen many strategies for doing this. Ive tried a few things including checking the time after a number of cycles and sleeping for the difference between the expected time and the actual time elapsed, but checking the time slows down the emulation by a factor of 8 so does anyone have any better suggestions or perhaps ways to optimize time polling in java to reduce the slowdown?

4 Answers4

2

The problem with using sleep() is that you generally only get a granularity of 1ms, and the actual sleep that you will get isn't necessarily even accurate to the nearest 1ms as it depends on what the rest of the system is doing. A couple of suggestions to try (off the top of my head-- I've not actually written a CPU emulator in Java):

  • stick to your idea, but check the time between a large-ish number of emulated instructions (execution is going to be a bit "lumpy" anyway especially on a uniprocessor machine, because the OS can potentially take away the CPU from your thread for several milliseconds at a time);

  • as you want to execute in the order of 1000 emulated instructions per millisecond, you could also try just hanging on to the CPU between "instructions": have your program periodically work out by trial and error how many runs through a loop it needs to go between instructions to "waste" enough CPU to make the timing work out at 1 million emulated instructions / sec on average (you may want to see if setting your thread to low priority helps system performance in this case).

Neil Coffey
  • 21,615
  • 7
  • 62
  • 83
  • yeah I'll give that a try and just track statistics for the cycle count. it might be a better way to go. Im thinking if i do that it would probably be best to inject a series of nops via a custom classloader unless you can think of a better jvm operation to use. –  Jul 03 '11 at 04:55
  • You mean from native code? That's worth a try as it might be a bit "nicer" on the system (a Java->native call takes approx 200 CPU cycles for what it's worth, so you need to factot that in too). – Neil Coffey Jul 03 '11 at 05:01
  • actually i meant the jvm nop instruction. By extending the classloader you can inject a sequence of jvm nops into an existing function(or create a nopping function using a jvm assembler like jasmin) this type of block should get cached by jitc and get executed as a set of native nops anyways without the overhead or system dependant code –  Jul 03 '11 at 05:09
  • I'm not sure the JVM NOP instruction gives you such guarantee (it may even just be ignored completely, i.e. generate no native instructions once JIT-compiled). – Neil Coffey Jul 03 '11 at 05:25
  • 2
    The JIT compiler is likely to optimize away any NOP bytecodes. – Stephen C Jul 03 '11 at 06:15
  • Regardless of my actual implementation, i think this is the best approach as it wont be as blocky and it will allow greater granularity in control. –  Jul 03 '11 at 17:18
  • Holding on to the cpu worked out the best. A Statistics engine running along side it increases or decreases a value as such. for(int i = value;i > 0;i--); this method seems to smoothly limit the cycles and gives up the cpu as needed thanks –  Jul 25 '11 at 19:13
  • Thanks for the feedback -- it's interesting to know that this worked as expected in the end! :) – Neil Coffey Jul 25 '11 at 19:34
2

I would use System.nanoTime() in a busy wait as @pst suggested earlier.

You can speed up the emulation by generating byte code. Most instructions should translate quite well and you can add a busy wait call so each instruction takes the amount of time the original instruction would have done. You have an option to increase the delay so you can watch each instruction being executed.

To make it really cool you could generate 6502 assembly code as text with matching line numbers in the byte code. This would allow you to use the debugger to step through the code, breakpoint it and see what the application is doing. ;)

A simple way to emulate the memory is to use direct ByteBuffer or native memory with the Unsafe class to access it. This will give you a block of memory you can access as any data type in any order.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • thanks for the suggestion but im actually using System.nanoTime() already. Its too much overhead to call as frequently as i need it, and doesnt have guarenteed accuracy anyways. Hence the statistical filter. As for the idea of generating 6502 assembly and translating it to JVM code, this follows under the area of static recompilation or dynamic recompilation emulators which is problematic for genetic code and not really ideal for my situation. But as i mentioned, Its running fast enough already for my needs –  Jul 03 '11 at 17:07
  • Using nanoTime() should give you sub-microsecond accuracy which should be all you need for a 1 MHz simulator. – Peter Lawrey Jul 03 '11 at 21:16
2

You might be interested in examining the Java Apple Computer Emulator (JACE), which incorporates 6502 emulation. It uses Thread.sleep() in its TimedDevice class.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
0

Have you looked into creating a Timer object that goes off at the cycle length you need it? You could have the timer itself initiate the next loop.

Here is the documentation for the Java 6 version: http://download.oracle.com/javase/6/docs/api/java/util/Timer.html

dvanaria
  • 6,593
  • 22
  • 62
  • 82
  • N.B. Internally, Timer just does effectively what the poster said of sleeping till the next expected time, and you'll generally only get resolution to the nearest 1ms at best. – Neil Coffey Jul 03 '11 at 04:35
  • True, but i don't need to sleep between every cycle though. Im waiting for a few thousand cycles and then sleeping for the difference between expected time and actual time. i thought this would be enough to solve my problem but either somethings wrong with my implementation or the idea is intrinsically flawed –  Jul 03 '11 at 04:43
  • I do like the idea of handling the pausing in a separate thread and im looking more into that –  Jul 03 '11 at 04:44