Something simple to emulate


Just my internal notes, nothing useful to anyone else, but my fumbles so far..

3rd September, 2013

18:52 Create Visual Studio Project
18:54 Reading http://en.wikipedia.org/wiki/CHIP-8
19:00 Implement switch table for instructions
20:00 Find a ROM to run through our disassembler
20:10 Pong rom seems tiny! Is this right???
20:20 Running pong through asserts on bad instruction
20:25 Trying to find out endian of machine
20:27 Assuming endian flipping required, it then generates 16 sensible instructions:

Set v10 to 2
Set v11 to 12
Set v12 to 63
Set v13 to 12
Set i to 0x2ea
Draw sprite at v10,v11 width 8, height 6
Draw sprite at v12,v13 width 8, height 6
Set v14 to 0
Call sub routine to 0x2d4
Set v06 to 3
Set v08 to 2
Set v00 to 96
Set delay timer to v00
Set v00 to value of the delay timer
Skips next instruction if v00 equals 0
Jump to 0x2cd

It then read garbage (it doesn't follow jumps at this point, so it would
appear some non instructions follow before 0x2cd or 0x2d4)

From Wiki:

Memory : 200h to FFFh <-- So jumps to 0x2cd/0x2df are possibly assuming ROM loaded at 0x200 ?

20:31 Thinking it would be best to allocate a chunk of memory, then index into this
memory, so first instruction would be at g_Memory[ 0x200 ]
20:34 Also wondering if endian flipping required for addresses in format NNN
20:35 Start fixing up instructions
20:47 Quick debug revals sub routine address of 0x2d4 so don't need to endian flip
20:53 Realised this rom (which seems tiny) pong.ch8 is 0x200 -> 0x2f5 so address
0x2d4 is past end of ROM, hmmmmm
20:57 Maybe 0x2d4 is a byte offset, not a 16 bit pointer offset.
21:03 With byte offset addresses we end up with recurisve sub routines calls to 0x200..
21:06 Recursive 0x200 calls just a bug, however 0x2d4 isn't going to valid instruction,
checked rom in hex editor and offset 0xd4 has data, must be doing something stupid
when loading.
21:11 ROM loading wrong, trying to work out why (haven't used bare C IO for ages)
21:18 Ah, "r" vs "rb" when opening file...! Has anyone ever not wanted the 'b' version?!
21:19 Doh, 0x2d4 is of course valid (less than 0x2f5 was getting confused by bad fread)
21:20 Sub routine seems to call propery now with stack pointer being restored, only
implemented half a dozen instructions so far.
21:34 Code gets as far as waiting for delay timer to reach 0, although timers are
implemented yet. Going to implement some more of the easy instructions first.
21:36 Stopping for dinner.
22:30 Looking into carry/borrow flags.

Thoughts:

Carry:

u8 a, b;
u16 r = a + b
reg carry = r > 0xff
u8 result = r & 0xff

Borrow

u8 a, b
u8 result = a - b
reg borrow = b > a

22:40 Implementing sound/delay timer registers
22:50 Now have program waiting for input.
23:15 Implemented everything except:
- Screen clear
- Calls RCA 1802 program at address (different from jump or sub routine call how?)
- Draw sprite
- Skip instruction if key pressed
- Skip instruction if key not pressed
- Await key press and then store
- Set I to location of 4x5 font sprite for character in reg X
23:16 Finishing up for the night, would like to look at getting some GFX on screen next.
 
@crow_riot completely off-topic but I recently added Crossbridge support to our engine at work, so we can now run all of our games in a browser albeit within a Flash window. I did very briefly look at Emscripten but had some difficulties getting it up and running on my Windows box (surely possible, but I decided to look at Crossbridge first). There is something quite weird/magical about seeing your full games running in a browser window somehow. If you have good experiences with Emscripted please share, I am still tempted to add support for it at some point, I love the idea of being able to run our games in a browser with no plugin.
to not derail this thread, check out my post here http://boards.openpandora.org/index.php/topic/14208-emscripten-quick-howto/
 
Last edited by a moderator:
My random idea : what about coding a Corewar interpreter and starting a competition here ^^
 
I never really got Corewar. It always seemed too random to me. Now something like RoboCode could be interesting, if it weren't Java.

In fact, I had an old Windows game which was similar to RoboCode, you turned the turret or the tank, shot, and you even had limited shields. You also had maps... I wonder if I can find the program at home, might work through Qemu
 
 
Corewar is about taming randomness :p .

We had to code an interpreter and a compiler when I was studying, then we had a "bot" competition. I found this exercise very instructive.

I can't think about anything simpler related to emulation and it may be a fun thing to do. It is a good introduction.

(Edit : there is still brainf**k)
 
Last edited by a moderator:
(Edit : there is still brainf**k)
My Bf interpreter (full source code!)

10 REM Brainfuck interpreter
20 LET c$="++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
30 BANK NEW bfdat,32768
40 LET i=0: LET d=0
50 INC i: IF i>LEN c$ THEN STOP
60 CASE c$(i)
70 WHEN "<": DEC d
80 WHEN ">": INC d
90 WHEN "+": LET v=PEEK(bfdat,d): INC v,1,0 TO 255: POKE bfdat,d,v
100 WHEN "-": LET v=PEEK(bfdat,d): DEC v,1,0 TO 255: POKE bfdat,d,v
110 WHEN ".": PRINT CHR$(PEEK(bfdat,d));
120 WHEN ",": INPUT b$: POKE bfdat,d,CODE b$
130 WHEN "[": IF PEEK(bfdat,d)=0 THEN GO SUB 170
140 WHEN "]": IF PEEK(bfdat,d)<>0 THEN GO SUB 210
150 END CASE
160 GO TO 50
170 LET b=1
180 INC i: IF i>LEN c$ THEN STOP ELSE IF c$(i)="[" THEN INC b
190 IF c$(i)="]" THEN DEC b: IF b=0 THEN RETURN
200 GO TO 180
210 LET b=1
220 DEC i: IF i<1 THEN STOP ELSE IF c$(i)="]" THEN INC b
230 IF c$(i)="[" THEN DEC b: IF b=0 THEN RETURN
240 GO TO 220

Edit: Now with better, more efficient code :)

D.
 
Last edited by a moderator:
So, first of all the development diary for tonight

4th September, 2013

18:45 Grab SDL and get a window hooked up

19:15 Adding font and putting in some basic rendering code

19:50 Have a couple of bats and a ball that moves, everything is flashing when moving, not sure

if that is how it is meant to be or not at this stage

19:51 Need to limit instruction speed, going to limit while loop to match target CPU

19:56 Looking at input

20:10 Can move bats up and down, however collision not working, could be first tricky to debug

problem!

20:11 Tracking down scores not rendering right

20:25 Found bug, when setting i to memory location of font character, I was instead setting I to be

the g_Memory[ offset ] instead of just offset. Get scores, bats and ball on screen now.

20:26 Stopping for dinner.

21:15 Trying to find spot code executing that controls collision...

21:23 Fixed collision, when deciding if borrow flag should be set, was doing it after the subtract

was performed, so it was making the decision based on the new contents of the resultant reg.

21:25 P1 score goes up, but P2 doesn't, investigating.

21:35 Fixed scores, the instructions to store regs -> mem and mem -> regs looped around one too

few times.

21:40 Make quick video.

So after 5 of 6 hours, I am at the point where my emulator runs a ROM! Only I suck at playing both paddles of Pong and holding a camera at the same time.

https://www.youtube.com/watch?v=qLx1_mJJWE4

Still a few things left for another day...
 
You lot have whetted my appetite now :)

This is my old Chip8 source for the emulator core - I'm very tempted to add sound and finish it now :)

It's not the highest quality there is (uses a nasty hack to reverse the endianness) but it was written a while ago :)

D.
 
Last edited by a moderator:
@sebt3 Thanks, hopefully tonight I'll be able to fix up the final bits so it can be tidied and committed properly.

@ZXDunny Looks like you have some HP48 support going on in there too?

I have purposely not looked at any source for other Chip8 emulators (up until taking a look at ZXDunny's) as I find once you have seen how something is done, it is very hard to then do your own version without doing things almost the same as the code you looked at. With this in mind, it is interesting to see how similar the ZXDunny's source is to mine (with all things considered, it isn't really surprising, given how straight forward the Chip8 machine is, it makes sense to do things in a specific way). There are certainly a couple of tricks employed by ZXDunny which I considered, but decided against and even now I'm not sure which way I would do it on the next project. For example, this (from ZXDunny):


Opcode := GetWord(PC);

N := Opcode And $000F;
NNN := Opcode And $0FFF;
KK := Opcode And $00FF;
X := (Opcode And $0F00) Shr 8;
Y := (Opcode And $00F0) Shr 4;

That is done once at the start of each loop, where as I extract the variables on a per instruction basis. The pro's for ZXDunny's implementation is less code and a lot neater (just done in one place), the reason I decided against this is that I wanted to limit the scope of these variables, the idea being that if an instruction is meant to have access to X, Y and NN, I didn't want to be able to accidentally access X, Y and KK by mistake. So my code looks more like:


case 0x3000:
{
int reg = ( instruction & 0x0f00 ) >> 8;
int val = instruction & 0x00ff;
if ( g_Cpu.Regs.v[ reg ] == val )
pc += 2;
}
break;

In other cases I might extract 'reg1' and 'reg2', but in the above snippet the only variables in scope are reg and val, so I can't accidentally attempt to use 'reg1' instead of 'reg'. For the next project, I think I am tempted to go for a hybrid, so have a few macros for extracting N, NNN, KK, X, Y and then use them in each case to keep limited scope.

I have a couple of questions, but I will hold on to them for now, as I want to have a shot at finishing off my emulator and see if I can answer them myself.
 
Hehe, I thought that might come up - "Bloody hell, that's a bit sub-optimal" I thought when I read through my code... But hell, you live and learn, I suppose.

I did that because I was trying to get the most readable code I could, and because there is a very big limitation of Delphi/Pascal right there:

You can declare variables that are local only to that level of code (the case element) whereas I could only do that if I actually branch to individual procedures for each "opcode", as you can only declare variables local to a procedure or function in pascal. Despite my general hatred of all things C/C++, I actually like that particular feature!

And yes, I added HP48 support when I'd done the basics. Some of the info was very hard to track down though, so it took longer than I had intended.

Looking forward to your next updates!

D.
 
This actually is rather inspiring. Perhaps I'll have to take a shot at making a Chip8 interpreter in python or something :)
 
congrats for getting this running so fast :) somehow i'm jealous now ;)
 
@Moxie @crow_riot It is like Exophase said really, it is a bit abstract, the Wikipedia page describes each instruction, is isn't like you are working anything out, or doing any deep digging, it's like "Store the immediate VALUE in to register vREGISTER" or "Add register vREG1 to vREG2 and store result in vREG1". It feels more like a homework assignment than anything else.

I have some satisfaction of seeing something running though and even more when I can drop other ROMs in and see them running also. I have certainly learned a few things along the way, things which are stupidly obvious to anyone who has written an emulator, and to be fair, probably obvious to most people who haven't, but either way weren't obvious to me at first, but then I had an 'ah hah!' moment and figured out a better way to do something. For example, initially I didn't allocate the systems memory and index into it, instead I had some memory for the ROM, some memory for the font, some memory for the data, and was attempting to map things in an ad-hoc manner. That said, if I started emulating architectures that have memory mappings (so physical memory is mapped to multiple addresses, or non contiguous addresses or whatever), I would need to do some mapping. Anyway, getting a little ahead of myself...

As with a lot of things, once you have done it once it is obvious; I could have read some other emulators source code first but one of the reasons I wanted to look into these projects was to have the 'ah hah' moments myself.

I think Chip8 was a pretty good suggestion to get some quick satisfaction, learn a few 'obvious' things, and get prepared for something a bit more interesting. So thanks for the suggestion and I certainly would recommend anyone interested who hasn't written an emulator before to dive in and have a go. Maybe if a bunch of people did this, in a bunch of months/years (or perhaps decades in my case) we might have a bunch more people on the boards working on cool emulators (that people actually want to use)!
 
Yes, well, at my current level of programming...I shouldn't say "proficiency", the correct word would be something like "familiarity". My skills are old and rusty - then a "homework assignment" feels just about right :)
 
I might have to go on a similar adventure one of these days - I've said it before, emulator code scares me... I probably need to do something like this so I can overcome the weird feeling I get when trying to understand it.
 
Emulation isn't hard - at least not at the level we see here (and for anything up to 16bit, really). It's just really tedious.

Chip8 contains a handful of opcodes. The z80 has around 700 or so, and each has to be coded for. Sure there's some shortcuts you can take, but it's still a huge amount of drudge. And you'd not be building a dynarec until you really hit the limits of performance. After you've got a core up and running, it's just a question of plugging it into a graphics and sound system (modifying your core if necessary to get hardware interactions emulated) and you're away.

I've done it myself a few times, and really enjoyed it. PandaBAS is actually an emulator.

D.
 
I think Chip8 was a pretty good suggestion to get some quick satisfaction, learn a few 'obvious' things, and get prepared for something a bit more interesting. So thanks for the suggestion and I certainly would recommend anyone interested who hasn't written an emulator before to dive in and have a go. Maybe if a bunch of people did this, in a bunch of months/years (or perhaps decades in my case) we might have a bunch more people on the boards working on cool emulators (that people actually want to use)!
Congrats, now next logical strep would be to try something more serious like Saturn or the joyful fun of Megadrive+MegaCD+32X combo. 5+ CPUs, bunch of custom chips with incomplete documentation all running at the same time, all for your synchronization enjoyment. Try to make it perfect, and you won't be bored for the rest of your life!
 
Back
Top