PDA

View Full Version : Chip 8



Pages : 1 2 3 [4]

Cyberman
October 17th, 2006, 19:31
Well my name is Kronox and i'm finally writing a Chip8 Emu...
The main core ---> opcodes , memory etc it's currently done.. the only thing i lack are the graphics [rofl] --> the most important thing though..

I really wanted to know.. How should i code my drawing routine?
I'm currently learning SDL .. but i don't really get how to "Combine" the graphics with the emu itself..

Any help will be apreciated [with examples :bouncy: ^^ ] AHhahah =]

[as you may notice i'm not a native english writer .. so please forgive my crappy english..]

Thanks hehe

Kronox!Ummm good question, your english is fine by comparison to some English people. :D
Please do not create new threads when an existing active thread already discuses the topic, if your question is not answered in that topic ask, in that topic. Otherwise there will be 50 threads about the same thing. I'm merging this thread with the Chip8 thread.

Enjoy.

Cyb

Kronox!
October 18th, 2006, 05:17
Garstyciuks: Thanks for all your help .. that was exactly what i was looking for :bouncy:

now!.. finally i can finish my emu :party:

Ahh And Cyberman as for creating a new thread.. Sorry it won't happen again.
And thanks for merging my thread with this one.

Good NIghts and thanks for all your help
Kronox!

-----
Saludos desde Chile ^^ jeje --> greetings from Chile hehe
P.S ---> As for my english... i feel flatered ^^ it all comes from games and emutalk hehe. so as long as you guys write well, i can improve it:bouncy:

doomangel
November 24th, 2006, 21:23
I'm quite new to emulation, and i decided to start making one.

I'm already done with making a disassembler, and the renderer has been initialized, but there are bugs:cry:

On 0x60 0x00 which is MOV i've made this code:

V[memory[pc]&0x0F]=memory[pc+1];

The code should move the value of memory[pc+1] to V[x], but it doesn't..

If someone could modify my code so that it would work, i'd be thankful:(

Cyberman
November 24th, 2006, 23:33
Welcome to emutalk!

I'm merging this to the Chip8 thread since this is related to the chip8.


There may already be an answer somewhere in that thread as well so if you search it maybe you will find it?

However on casual observation I believe that you should instead of reading the 'current PC' information for your opcode, you should store it as a local value.

IE instead of this (which not only is expensive it obfuscates what you are doing, a very BAD practice)

v[mem[PC] & 0x0f] = mem[PC + 1];
You should extract both the register location AND the value to store in it first.
IE

#define REGISTER(X) ((X) & 0x0F)
// fetch opcode
OpCode = ProgramMem[PC];
// advance program counter
PC++;
// for this opcode we need to extract a value to store
Value = ProgramMem[PC];
// advance program counter
PC++;
// compute the registerID/variable location
RegisterID = REGISTER(OpCode);
// move the Value into RegisterID memory location
VariableMem[RegisterID] = Value;
This makes your code VERY readable, it could save your job sometime.

By the way what is v? Vector Variable Register space? It's not obvious maybe you should try a less implicite approach to your variable naming?
I'm assuming you have program memory and Variable memory, I could be wrong. I suggest you make your code clear, to find mistakes easier. From personal experience you will spend more time debugging than writting code otherwise.


Cyb

hap
November 25th, 2006, 14:30
What Cyb said, but fetch instructions 2 bytes at a time, and use a mask to figure out what it means, eg. if ((opcode&0xf000)==0x6000) { do_stuff_with_it(); } (for speed, use a switch/case method). From a hardware point of view, some of these don't make much sense, but that's OK, since you're not emulating a CPU: you're handling an interpreted low-level programming language.

(V=CHIP-8 variable, official id for it)

virror
December 7th, 2006, 22:30
Hello, i have been visiting the Chip8 forum for quite some time now. And now i have started on my own emulator. Most stuff is working, but i have problem with fonts and some other stuff, can anyone check my draw function? I know its not the best draw around...


void draw(void)
{
char n = 7,a,b;
char antal = opcode&0xF;
V[15]=0;
for(b=V[((opcode&0xF0)>>4)];b<(antal+V[((opcode&0xF0)>>4)]);b++)
{
for(a=V[(opcode&0xF00)>>8];a<(8+V[((opcode&0xF00)>>8)]);a++)
{
if((screen[a][b]==1)&&(((mem[i]&(0x80>>a-V[(opcode&0xF00)>>8]))>>n) >= 1))
V[15]=1;
screen[a%64][b%32] ^= ((mem[i]&(0x80>>a-V[((opcode&0xF00)>>8)]))>>n);
n--;
}
}

for(b=0;b<32;b++)
{
for(a=0;a<64;a++)
{
if(screen[a][b]>=1)
{
Form1->Canvas->Pen->Color = 0;
Form1->Canvas->Rectangle((150+(a%64*4)),(100+(b%32*4)), (154+(a%64*4)),(104+(b%32*4)));
}
else
{
Form1->Canvas->Pen->Color = 15923696;
Form1->Canvas->Rectangle((150+(a*4)),(100+(b*4)),(154+( a*4)),(104+(b*4)));
}
}
}
}

Garstyciuks
December 8th, 2006, 13:26
If other graphics is being drawn alright, then everything is fine with the drawing function (although I haven't checked it :P), the problem with the text drawing may be in the BCD instruction or the instruction which sets index register to correct memory location which contains the graphical representation of some number.

Edit: I looked a bit at your drawing routine.

I found this mistake: you forgot to reset n to 7 after the for (a=.............) loop execution. It should be set above the for (a=........) loop.

And I wanted to give you some advices:

I think that


if((screen[a][b]==1)&&(((mem[i]&(0x80>>a-V[(opcode&0xF00)>>8]))>>n) >= 1)) V[15]=1;

should be screen[a%64][b%32]. And if I am not mistaking, && operator only returns true or false. So there's no need for >=1. It would be needed if you used & operator only. Also in the if (screen[a][b]>=1) >=1 is not needed, because the condition is always true when screen[a][b] has some other value than 0. You should also use some more variables, because it performs more calculations, than it should be doing. And it would be more readable and look nicer :). Here is some common calculations, which could be calculated once per cycle and stored in a variable:


V[((opcode&0xF0)>>4)]
V[(opcode&0xF00)>>8]
0x80>>a-V[(opcode&0xF00)>>8]))>>n

And in line Form1->Canvas->Rectangle((150+(a%64*4)),(100+(b%32*4)), (154+(a%64*4)),(104+(b%32*4))); %64 and %32 for a and b variables are not needed, because they never exceed those values.

virror
December 9th, 2006, 14:15
I know my draw code is ugly, but i cleaned it up a bit now, but dont get it when you say i have to reset the n variable. I only use it in the (a=...) for loop....

But thanx for the great tips.

Garstyciuks
December 9th, 2006, 14:35
What I meant to say is that n is never set back to 7. It should be reset to 7 here:



...
for(b=V[((opcode&0xF0)>>4)];b<(antal+V[((opcode&0xF0)>>4)]);b++)
{
n = 7;
for(a=V[(opcode&0xF00)>>8];a<(8+V[((opcode&0xF00)>>8)]);a++)
...

virror
December 9th, 2006, 22:28
Thanx alot! Now i get it, stupid mistake : p Now alot of graphical bugs are gone, and scores are slightly better, but still not good....

Edit: Are the two sub instructions going to give carry? The documents say no, but my emu are working alot better with them...

Garstyciuks
December 10th, 2006, 10:26
I think it should set it. Which documentation are you refering to? The one on wikipedia and on David Winters' documentation both say that it sets carry. From wikipedia:


8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.

virror
December 10th, 2006, 13:18
Found another error in my draw code, i forgot to increase I, so i keept reading from the same place in memory when i was going to draw many lines. This fixed my font problem, but now collision detection is broke instead....

Garstyciuks
December 10th, 2006, 14:29
Can you post your most recent code for the draw function?

Doomulation
December 10th, 2006, 15:24
I think it should set it. Which documentation are you refering to? The one on wikipedia and on David Winters' documentation both say that it sets carry. From wikipedia:

Set carry. The proiblem is that different docs say different, but the opcode should set VF if carry.

virror
December 10th, 2006, 19:55
Well i got it to work now, BRIX is working perfect now, or as perfect it can be without any keybord emulation.... But all other games have alot of bugs : p

http://www.ing.umu.se/~victor/Brix.JPG

Garstyciuks
December 10th, 2006, 20:05
That's the first game I got to work perfectly too :)

hap
December 17th, 2006, 14:33
Try to get this working perfectly ;)


Rush Hour for CHIP-8 by hap 17-12-06, http://home.planet.nl/~haps/
Based on a boardgame by Nobuyuki Yoshigahara "Nob" and ThinkFun,
http://www.thinkfun.com/

This game contains 170 puzzles. Most were taken from the original cardsets,
some were made by me, and some were generated with Michel's PyTraffic,
http://alpha.uhasselt.be/Research/Algebra/Members/pytraffic . Refer to
the source code for detailed information on this.

The game should work fine on any CHIP-8 interpreter. Moreover, it has been
tested on an RCA COSMAC VIP (4KB) emulator, so hopefully it will work on the
original machine too. Due to memory constraints, password generation was
removed for the VIP version.


FILES
=====

Rush Hour.txt this
Rush Hour.c8 game for a CHIP-8 interpreter
Rush Hour.vip game for the COSMAC VIP (includes the CHIP-8 interpreter)
rh.chp source code


HOW TO PLAY
===========

The goal of the game is to slide the arrow block(s) out of the 6*6 grid.
Completing 10 boards will be awarded with the inclusion of the next boardset(s)
as seen from the connections on the boardset select screen, a password will be
given so you don't have to complete the game in a single session.

HEX key PC key* Use
---------------------------
5 W up
8 S down
7 A left
9 D right
A Z ok/hold to slide
1 1 option(in-game)/back

* = for interpreters that have the 4*4 keypad at 1234/QWER/ASDF/ZXCV.

ShizZy
December 17th, 2006, 18:42
Nice hap.... won't run in mine, but I don't think I put more than 4 hours into my c8 emu :(

Doomulation
December 18th, 2006, 09:13
Hmmm, love to, but it seems the good ol' chip8 project doesn't compile anymore. Meh. Lots of work to do to get it working again.
And kudos to Microsoft who seem to have removed the old, good code from their SDK of newer versions. Reaaal nice.... now I have to hunt down old code or substiute it for something else, and frankly, I have no desire.
Anyone have any diutil.h, diutil.cpp, dxutil.h, d3dfont.h from the DX9 SDK lying around?

hap
December 18th, 2006, 09:45
Thanks ShizZy. It shouldn't be too hard getting it to work, CHIP-8 isn't exactly Gamecube ;)

I've never bothered to update the DirectX SDK, well.. I did once, but then the huge download stopped near the 100%, so yeah, I have those files. I see all the files you requested are from /Samples/C++/Common, attached is that whole folder.

Doomulation
December 18th, 2006, 10:41
Thanks!
I've lost my SDK a couple of times in the past and well... newer version sucks.
Hmmm, it must be a newer (or older) version than I had, but I found another function to solve my problem.

Well, I don't know how to play it or how it should look like, but here's how it looks for me.

nahteecirp
December 26th, 2006, 16:13
Hey, awhile ago I wrote up a simple Chip-8 challenge for the Ruby community ( Ruby Quiz - Chip-8 (#88) (http://www.rubyquiz.com/quiz88.html) ). It ran successfully there, but I was wondering how good you all thought the explanation was (like how understandable it was, or if I blatantly got something wrong). I figure it would be a pretty good as a down to earth explanation of Chip-8 for those looking to start a Chip-8 emulator, so I'm considering expanding it to cover all aspects of Chip-8. Like I said, just curious to what you guys thought of it. Thanks again.

Doomulation
December 26th, 2006, 18:13
First thing I found wrong at your page is this:


Emulation is where raw operation codes (the most low-level way of feeding instructions to a computer) are translated by another computer, giving a totally different system the ability to run programs not designed for it.

That's not right. Emulating means to simulate the workings of the target architecture, so that software designed for it can run on a different platform. That's how I see it. However, emulation does not need to duplicate exactly how the hardware does it; it simply needs to achieve the same OUTPUT.
Remember, the processor is just ONE component of an architecture.


1. Make the emulator read the file. All opcodes are four digit hex numbers,
but those will need to be split.
IMO, it's better to say 2 bytes. Hexdecimal or not, a CPU doesn't care. All opcodes are encoded in bits; it may just be a coincidence that the information can be easily extracted using hex.


0000 This is nothing. If you see this, it should mean that you are at the end
of the file and have nothing left to read, so you therefore need to exit
gracefully.
I'm unsure you should specify this. On languages like C/C++, when you read past the end of file, it doesn't write any data to the buffer at all. Plus, no chip8 game should ever reach the end of the file. If it does, then you've done something wrong. There is an opcode for terminating the system, however.

A note on most/least significant bits: these words can be confusing to people who haven't worked much with bit manipulating. Perhaps you should explain the terms...


Say we had the opcode 100F, that means we would jump to after byte 16 (it is a hex remember).
The jump opcodes specify the exact location in memory to jump to; it is NOT relative. The opcode above is invalid because it would jump to interpreter memory (which ranges from 0 to 0x200). A valid opcode might look like 0x120F.

As for you explanation on carry, I find it simpler to explain how we typically do it. We add or subtract normally and not bit-wise. We simply set carry if the registers loop. That is, if we subtract something and it becomes a negative number (under 0), or we add something and it becomes bigger than 255, in both cases which, due to the registers only being one byte unsigned, they loop.

I haven't checked your opcode table, though, but let me find my doc and I'll check them too.

dolqube
February 17th, 2007, 22:30
Nice Post Doomulation ;)

I especially liked

Quote:
That's not right. Emulating means to simulate the workings of the target architecture, so that software designed for it can run on a different platform. That's how I see it. However, emulation does not need to duplicate exactly how the hardware does it; it simply needs to achieve the same OUTPUT.


HLE until the end. :evil:

on another note I was thinking of writing a chip 8 emu today cos i was bored.
and gc emu (is hard!!!) ie. really long and boring.
I always seem to put it for weeks on hold.

_Zack_
February 21st, 2007, 13:50
im currently porting TJA's chip8 emu to the psp...

so far its at compiling stage with no errors...

just have to adjust the controls and screen etc now :D

a few other things too , rom browser etc

smcd
February 21st, 2007, 15:39
I'm going back to learning some basic graphics programming (SDL) so maybe I can finish mine finally - have had the the core done for a while just need graphics (and to set up input for SDL)

_Zack_
February 22nd, 2007, 22:59
YAY, real progress today, my emu port (to PSP) is now running pong (harcoded to play one rom so i havnt tested more) which seems full speed. :D

now to add control input :)

Cyberman
February 23rd, 2007, 04:35
You know this looks like something fun to port to the Palm.
Hmmm Apart from all the other wacked projects I have this looks like fun. I'll schedule it in a few months. :D

Cyb

_Zack_
February 23rd, 2007, 06:53
damn input, i never imagined it would be so tiresome..

13hrs and still no shagging input :rofl:

only took 5 to get roms working lol :P

edit : input is working :P finally lol

out of pong and connect4 . imput is only working on connnect 4 ...

the two games seem to use different checks in the code for input so im going to have to do a bit more work yet ;)

_Zack_
February 24th, 2007, 20:25
sorry for double post

thought id show you guys the emu so far ;)

Im re-writing it all from scratch though as i dont like the way it was done, so lets hope it doesnt take too long :D

edit - inputs working great now :) just have to touch it up somemore and il release a update :)

btw i called it dreamchip because it was never given a name and i thought dreamchip suited it :P

_Chrono_
February 25th, 2007, 10:20
Hey guys. ShizZy kinda inspired me to finally do my Chip8 emu.
All I can offer right now is a screenshot and an explanation.

First off I wrote my emulator in Microsoft Axapta 3.0 (latest version being Microsoft Dynamics AX 4.0) using X++. How cool is that! :p The software itself is an ERP and is in no way geared towards this kind of coding.
The emulator is slow as hell... in part because the language sucks for this kind of stuff and because I'm running through a remote desktop on a slow server.

I would provide source code but I doubt any of you can actually import the project and try it out. Aniway here's a shot. Most games boot but I do have things to add like fonts and input. Still bugs to iron out since UFO crashes.

virror
February 27th, 2007, 19:47
Can anyone check my code please? I have a bug that makes the drawing looks like shit in some games...

www.ing.umu.se/~victor/code.txt

Falcon4ever
February 27th, 2007, 20:12
zion: How did you do the drawing part for the psp port?

I can imagine doing smth with putpixel would be incredibly slow...

(hehe thats how my current chip8 emu on windows does it... ah well... its like an inbuild gamespeedlimiter :whistling )

_Zack_
February 27th, 2007, 22:58
the drawing code was mainly unchanged, just a few lines of code modified.

It does draw slow though and im going to do it a better way once ive finished my other aims for the emu :)

thanks for the question :D

this thread is coming alive once more :D

_Zack_
March 3rd, 2007, 02:54
I have worked real hard over the last week with the emulator.

The controls now work great with every game that was tried.

There have been various other bugfixes too.

More roms now work, tetris being one.

Source code is released under the gpl licence (i think) and is included in the zip file.

Next release will see a filebrowser and a speed up and some games are running a bit slow.

Thanks :)

Comments appricated ;)

Falcon4ever
March 3rd, 2007, 10:07
ah you're just using SDL with putpixel :P... so you dont need a framelimiter cuz its slow from itself :P

_Zack_
March 3rd, 2007, 14:08
yeah i need to speed up the drawing routine as its not full speed on the psp yet :P

_Zack_
March 24th, 2007, 22:36
I just changed my os to ubuntu linux (edgy) v6.11 so i decided to apply the opimisations i made for the psp version (faster drawing) and make it for linux :)

Ive included the binary and source, need to implement a frame limiter into the linux version. psp version is running at around 65% speed.

enjoy :P i hope this can bring some life back into this thread ;)

Kronox!
April 30th, 2007, 04:10
I hope that too. Once again i got hooked into emulation and coding, now i'm back with a little more understanding on the lang and currently writing a chip8 emu. I undertand the basic layout for the CPU and currently have all the opcodes necessary to emulate the Chip8 [Schip not yet]. But i feel a little dumb 'cause i'm looking at Aprentice's source from time to time [and i feel like a cheater xD] but that's not the point.. the point is.. I really don't understand how i can get the CPU loop running and more important i don't know how to do graphics routines so.. even if i understand what the opcodes do, i can't get my emu to show anything..

Is there any simple way to achieve graphics?
how do i set the CPU loop? I mean to read the opcodes and do the decoding process...

^^ Like you see i'm a newby [not only in coding, but in eglish writing as well] can you can give me a hand or push me in the right direction please?

thanks.. I now emulation it's not a simple task but i wanted a challenge[and i got one xD] [Btw Logical operators, pointers,shifting and all the stuff for opcodes are not issues atm][but not knowing how to set a drawing routine is really dissapointing..

Thanks in advance

Kronox

Garstyciuks
April 30th, 2007, 07:11
To get your emulation loop running, you need to load a chip8 ROM into your RAM, starting at adress 0x0200 (first 512 bytes are reserved for interpreters, and they are generally unused by emulators. I personally use some of those bytes for storing font data). Then you should start executing the ROM at adress 0x0200. If you think that your drawing code is OK, then do a little debuging and test out if all the arguments for the drawing opcode are alright, check out what image data I register points to. When I first was programming my emulator, I had forgotten to redraw my emulation screen and I didn't get any video data from my emulator. So you should also check your interface code :) I have attempted to explain the drawing opcode here (http://www.emutalk.net/showpost.php?p=359962&postcount=748). The code there isn't really good, I have reprogrammed my emulator, but haven't posted the code anywhere yet. The explanations might help you in some way. I'm using 128x64 vram array (I set 2x2 rectangles for chip8 pixels in chip8 video mode), to keep compatibility with schip games.

Kronox!
May 1st, 2007, 02:15
Thanks for the quick answer.
Even if i understand what you said about the starting adress and rom loading, i still can't figure how i can write it in code.

As for the drawing routine, thanks... the answer was exactly the same you gave me the last time xD but now i'm a little more mature in coding and i understand how to implement it with my own variables now^^ [thanks 'bout that]

If you can show me an example to set up
A) the main loop --> dunno if i have to use Do, For or other ciclic task operator..
and B) memory set up in 0x0200 T_T --> sorry if i'm asking newby questions, but i don't know what else to do..

Thanks for your time once again

Kronox

Garstyciuks
May 1st, 2007, 08:19
This is my LoadRom function:


FILE *pFile;
pFile = fopen(rom,"rb");
int size;
fseek(pFile, 0, SEEK_END);
size = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
fread(&mem.memory[rom_base], size, 1, pFile);
fclose(pFile);

cpu.flags |= CPU_FLAGROMLOADED;

rom is a const char *, it is the file name of the rom, given to the function as a parameter. rom_base is initialized to 0x200 when the application is initialized. cpu is a class for emulating all the opcodes. The flags part is only used inside my emulator, to specify specific states of my emulator. And this is my "main loop", it's in idle event of wxWidgets:


for ( int i = 0; i < 30; i++ ) chip8.Step();

if ( chip8.ShouldRedraw() )
{
SDL_Rect rt;

rt.x = 0;
rt.y = 0;
rt.w = 128 * iSize;
rt.h = 64 * iSize;
SDL_FillRect( screen, &rt, 0x00000000 ); //clear the screen

rt.w = iSize;
rt.h = iSize;
for ( int j = 0; j < 64; j++ )
{
for ( int i = 0; i < 128; i++ )
{
rt.x = i*iSize;
rt.y = j*iSize;
if ( chip8.mem.vram[ j ][ i ] ) SDL_FillRect( screen, &rt, 0x00FFFFFF );
}
}
}

iSize is an integer, which stores the setting of pixel size. It can either be 1, 2, 3 or 4. chip8.Step() function executes 1 opcode. chip8.ShouldRedraw() returns true if a draw sprite opcode (0xDXYN) was executed since ShouldRedraw() was last called. This helps to save some of the CPU usage from having to redraw every time. And in for ( int i = 0; i < 30; i++ ), 30 is a number that I came up with. The emulation is too slow if you execute 1 opcode per one idle event, so I execute 30 opcodes per idle event. This is a big hack, but I haven't come up with any better way of timing using wxWidgets. If anyone knows a better way to do timing with wxWidgets, then I would be glad to hear about it. :)

smcd
May 1st, 2007, 22:09
Mine looks something like this:


int main()
{
initcpu();
loadRom("pong.ch8");
while(1)
{
cpu_cycle();
Sleep(250); // this is a really really bad way to do things
}
return 0;
}

Exophase
May 1st, 2007, 22:20
And in for ( int i = 0; i < 30; i++ ), 30 is a number that I came up with. The emulation is too slow if you execute 1 opcode per one idle event, so I execute 30 opcodes per idle event. This is a big hack, but I haven't come up with any better way of timing using wxWidgets. If anyone knows a better way to do timing with wxWidgets, then I would be glad to hear about it. :)

I haven't used wxWidgets, but I looked into it some..

From what I could find on the idle events there's nothing to indicate that it's ran at a fixed interval of time. It could very well depend on where it's being ran, and you don't want that. Actually, what appears to be happening is that the idle event is being called constantly so long as there are no other events, which you also don't want to base timing off of.

Looking further I found this:

http://www.wxwidgets.org/manuals/stable/wx_wxupdateuievent.html

It appears you can setup an event which does go off at a given interval, which is exactly what you want.

30 is a pretty arbitrary number indeed, and in this case, a pretty small one. What you should do is pick a frame rate then set the timer around that. Something around 16 or 32Hz would be good, then you'd have say, 31-33ms intervals for the timer event. Here you would run N instructions - whatever N is will determine the clock speed. Chip8 doesn't have an actual clock speed, but something around 512KHz or higher should be far more than sufficient (especially considering it'd be executing one instruction per clock cycle, which was unheard of for processors of that day). For 32Hz updates, that'd mean 16384 instructions processed per screen update. You could use lower values, of course, but too low and the emulation will become slower or fail outright.

As far as I remember from chip8, the timing is more regulated by input than by video, so actual timing "accuracy" is probably not that important, but even vast inconsistency in screen updates might end up being noticeable.

By the way, since this thread is on the topic of timing again, I wanted to address something that I meant to almost a year ago but never did:


What bcrew1375 said is wrong, because: say that your PC is able to execute two times faster than your desired intructions per second, and as soon as the emulator finishes executing your desired instructions/sec, it will halt and wait until that second is passed, which will cause a desynchronization on timing.(The emulator will execute the instructions/sec under half of the second, which will cause the emulator to double the desired speed on by half a second, but the other half will just wait until the other second, and that's not what you want).

Secondly, 100 instructions per second is too slow, try 500~800.


Let's say you want to execute 1,000 instructions per second, then, after EACH instruction, you will have to wait until 1 millisecond has passed before executing the next instruction.


Time should be checked on every instruction(if you want it to be as accurate as specified), not every second.

I'm quoting this now because looking over things (including the most recent example) I see a lot of people falling into this dangerous line of thinking.

First, you really can't synchronize per instruction, for anything with realistic operating speeds. Precision and accuracy for timers is just too low, and the overhead to calling the timers alone would give you a big additional cost.

Second, there's no reason at all to do so. When emulating a machine, what's most important is first and foremost keeping the virtual time of the machine in order. What this means is that internal events should occur in the correct order relative to each other; everything is synchronized properly to an internal clock. For Chip8 this doesn't really apply because there aren't internal events (least of all interrupt causing ones), but for almost any real platform it would. Keeping virtual time consistent has nothing to do with keeping consistency with real time.

When it comes to emulating a machine, the only way a user can determine if it's running at correct speed or not is by observing that machine's external output. All machines are limited in how much external output they can display per unit time: by refresh rate (maybe per scanline), audio frequency, and so forth. Audio in particular is timed correctly by the hardware of the platform you emulate on, so the only thing that becomes an issue is minimizing latency, where you have a much bigger window than frequency (which is why audio buffers are larger than one sample). These operating intervals are on the order of Hz, not the KHz or MHz ranges that internal intervals may run at.

And even if the device's output rate wasn't the limiting factor human perception would be. As humans we certainly do not perceive things higher than in the Hz ranges, change something too rapidly and it blurs together (hence interleaving on displays and why pulse width modulation works for audio output), change timing slightly and people won't even be able to notice. So long as it catches up in the end (global time consistency) it really doesn't matter much.

Garstyciuks
May 3rd, 2007, 08:19
Thanks, Exophase, for your explanations. When I will have some time, I will improve my timing.

Kronox!
May 4th, 2007, 05:14
thanks as well. This weekend (when i have some time to spare) i'm going to work on my emu. Thanks for all the help guys... see you around

Kronox

givemeachance
July 23rd, 2007, 17:22
Hi all ! , im a new member , so dont bite me :)!

Im new into this subject(emulation) , and i have some questions to ask! , please
answer some of them when you find some time.

...ready? .. lets go !


1)I really wanna know , how does an emulator works!
I mean , when you load a rom into memory , then what checks you do?


2)How can i read a command set from a memory location?
and how can i edit it?


3)How do you load a rom file into memory?
and what's in the file ? commands ? i dont get it!


4)In the main loop what checks you do ????



I've programmed 2 mini games in c++ and opengl , so , i'll program the emulator in c++ , and
opengl as graphic api.


Waiting for your support!


thanks!

Doomulation
July 23rd, 2007, 17:48
1)I really wanna know , how does an emulator works!
I mean , when you load a rom into memory , then what checks you do?

What checks do you do? That's really up to you. If it's a game, then it contains instructions (and possibly, in case of others systems, a header, but chip8 does not).


2)How can i read a command set from a memory location?
and how can i edit it?
Just read the entire file and fill it up in an array of memory set to the amount of bytes the chip8 memory consists of. Then, should you want to read/edit/do whatever at local 0x?, then just read/do whatever at your array at position 0x?.


3)How do you load a rom file into memory?
and what's in the file ? commands ? i dont get it!
Just dump the contents of the file into memory. All of it. Read every byte and put it into the emulator's memory - the image consists of instructions, data specific to the game, and optionally (though not on chip8), a header.
Nevertheless, it's all loaded into memory - the entire file.


4)In the main loop what checks you do ????
In the main loop, you'll read 2 bytes, interpret the instruction and execute it, optionally redraw the screen (can also be done after a draw command), check delay timers and beep if 0, store key input and repeat.
That's what I can remember on top of my head anyway.

If you really have no idea how it works, then there's many open source chip8 emulator out there that you can study to get the idea.

givemeachance
July 23rd, 2007, 18:47
Thanks for the reply !

I'll start doing some tests, and i'll be back!

d0u6
July 25th, 2007, 20:35
I'm finishing my chip8 emu (Dchip8). I did the graphical interface in Opengl, and the code compiles in Linux and Windows. Only the sound is still missing.
My code is very simple. Even a kid can understand.

Until the weekend i'll post here.

[]'s

_Zack_
July 25th, 2007, 22:49
I'm finishing my chip8 emu (Dchip8). I did the graphical interface in Opengl, and the code compiles in Linux and Windows. Only the sound is still missing.
My code is very simple. Even a kid can understand.

Until the weekend i'll post here.

[]'s


Nice work :)

Post it here when your done :D

givemeachance
July 26th, 2007, 05:10
I'm finishing my chip8 emu (Dchip8). I did the graphical interface in Opengl, and the code compiles in Linux and Windows. Only the sound is still missing.
My code is very simple. Even a kid can understand.

Until the weekend i'll post here.

[]'s

Nice!!
When you're ready , can you please post the source?
I really wanna see how your app works :D


edit:
By the way , can you tell me how you render the tiles?
With resized quads? or with another way?:

Garstyciuks
July 26th, 2007, 08:00
I used quads for each pixel. My chip8 emulator has 4 zoom modes, so you can choose from 1x1, 2x2, 4x4 and 16x16 sized quads.

givemeachance
July 26th, 2007, 10:18
Nice , now i get it(at least how the rendering is done).

Thanks!, and btw , i really like your emulator!(just tested it)

Garstyciuks
July 26th, 2007, 11:28
I am glad that you like it :)

givemeachance
July 26th, 2007, 13:45
Can i ask all emu authors a question?

Can i take some parts of your code , and try to learn from them?
Of course , you'll be in credits!

d0u6
July 26th, 2007, 14:00
Nice!!
When you're ready , can you please post the source?
I really wanna see how your app works :D


edit:
By the way , can you tell me how you render the tiles?
With resized quads? or with another way?:

My screen is a vector Screen[64*32]. I created a 640x320 window and the function that show the screen do this:



for (i=0; i<32 ; i++) //for all lines...
{
for (j=0; j<64; j++) // ...and rows
{
if (Screen[j + i*64]) // if there is a pixel to draw....
{
glPointSize(10.0); //set the pixel size
glBegin(GL_POINTS); //draw the pixel on the screen
glVertex2i(i*10,j*10);
glEnd();
}
}
}



It still needs some fixes, but is basicaly this.....

My Sprite function:



void sprite(BYTE pX, BYTE pY, BYTE Length) /*drys*/
{
BYTE lineY=0, lineX=0;
BYTE data,offset;

if (V[pY] + Length > 32) Length = 32-V[pY];

for ( ; lineY<Length; lineY++)
{
data = Memory[I + lineY];
for (offset=0 ; data!=0; data<<=1, offset++) //set screen until no more pixel remain in data
{
if (data & 0x80)
{
if (Screen[(V[pX]+64*(V[pY] + lineY) ) + offset] == 0x1) V[15]=1;
Screen[(V[pX]+64*(V[pY] + lineY) ) + offset] ^= 0x1;

}
}
}
}

givemeachance
July 26th, 2007, 17:23
Hmm .. nice stuff.
I'll try with quads first , and i will also try with line points.
If im not mistaken , quads are faster....

LotsArs
July 26th, 2007, 18:21
My screen is a vector Screen[64*32]. I created a 640x320 window and the function that show the screen do this:



for (i=0; i<32 ; i++) //for all lines...
{
for (j=0; j<64; j++) // ...and rows
{
if (Screen[j + i*64]) // if there is a pixel to draw....
{
glPointSize(10.0); //set the pixel size
glBegin(GL_POINTS); //draw the pixel on the screen
glVertex2i(i*10,j*10);
glEnd();
}
}
}



Make sure you put your glPointSize(), glBegin() and glEnd() outside any loop and you'll save some GPU time. ;)



glPointSize(10.0);
glBegin(GL_POINTS);
for (i=0; i<32 ; i++)
{
for (j=0; j<64; j++)
{
if (Screen[j + i*64])
{
glVertex2i(i*10,j*10);
}
}
}
glEnd();

Doomulation
July 26th, 2007, 21:02
Can i ask all emu authors a question?

Can i take some parts of your code , and try to learn from them?
Of course , you'll be in credits!

Go ahead. I'm sure lots of them wouldn't mind, if you credited them. Lots of emulators have the source available.
My own emulator, Chuit, for example is one such. Zophar has both binary and source if I recall correctly.

Garstyciuks
July 26th, 2007, 21:29
I remember that my chip8 emulator couldn't properly emulate hap's sokoban game. So I have written a new emulator. I had this code written more than half a year ago, but didn't post it, because it isn't as complete as my other emulator. It still lacks some final touches, but the emulation part of it is fully finished. I am releasing it now, because I don't have enough time to work on it. While the emulation part of it is platform independent (I'm not sure about macs though), the frontend of the emulator uses wxWidgets and SDL. I can successfuly compile it on windows and linux with no changes in the code.

This emulator has better emulation code, while the other one is more 'complete' and has a debugger. So for studying emulation of chip8/schip, I would suggest reading the code of this emulator.

The controls are 1234, qwer, asdf and zxcv.

I will only provide source code for it. There are project files for MSVC 8.0 and Code::Blocks. If you decide to compile it manually, you need to link with wxWidgets (version 2.8.0 or higher) and SDL.

I don't mind people using my code as long as they don't make money from it :)

givemeachance
July 27th, 2007, 16:12
Nice thanks for sharing the source!, i've made some progress while checking out your source.

Im only a little confused with binary math...so please , someone answer my questions!


1)
What does this part of the code?


(flags & a_Random_flag)


2) Also , here ... what's happening?
the programmer removes the flag from the list??


flags &= ~CPU_FLAGWAITING;


3)
LOL , what does this part of the code?


(mem->memory[pc+0]<<8)



4)


memset(mem->vram, 0, 128*64);

Is similar to this :



for y = 0 to 64
for x = 0 to 128
vram[x][y] = 0;

???

5)
Why you need modulo here?


x_pos = ((x+i)%64)<<1;
y_pos = ((y+j)%32)<<1;

Garstyciuks
July 27th, 2007, 16:36
1) This is probably from some if conditions. It checks if some specific flag is set, or not.
2) it removes CPU_FLAGWAITING from the flags.
3) UINT16 opcode = (mem->memory[pc+0]<<8) | mem->memory[pc+1];

This piece of code loads the opcode from the chip8 memory into unsigned short variable. Say if memory[pc+0] is 0xD4 and mem->memory[pc+1] is 0x45, then opcode would be equal to 0xD445. And then this value is processed in the switch statement.

4) yes, the code is simillar
5) because the screen is 64 x 32 size. If the program is trying to draw outside the screen, it wraps it to the other side.

givemeachance
July 28th, 2007, 05:44
Thanks!
About question 3.
Why you shift the number? is there any reason???

Garstyciuks
July 28th, 2007, 07:40
Yes. memory is an 8bit array, and opcode is 16bit. I shift the bits, so they make the higher part of the 16bit number.

givemeachance
July 28th, 2007, 12:06
I think i get it.
So , here:


if (reg[(opcode&0x0F00)>>8] == reg[(opcode&0x00F0)>>4]) pc+=2;


This part is the fixed opcode value:


reg[(opcode&0x0F00)>>8]


And this one:


reg[(opcode&0x00F0)>>4]

The fixed memory value?

...Sorry for my newbie questions...............

Doomulation
July 28th, 2007, 13:54
reg[(opcode&0x0F00)>>8]


Contents of register X as specified by the opcode. If the opcodes look like this: 0xABCD, then it's the contents of register 0xB (or 11 to all you non-hex understanders).





reg[(opcode&0x00F0)>>4]

The fixed memory value?

Contents of register Y as specified by the opcode. Again, using the above example, it's register 0xC, or 12.

krypt100
July 29th, 2007, 11:10
Alright I have decided to release my SChip8/Chip8 emulator after it has been sitting on my hard disk for months . It is written in JAVA and supports most of SChip8/Chip8 programs (except for some programs that end with .c8x), most of the game fixes(Hwrap,Vwrap..) that I use for my emulator come from Giedrius's, I already included all the credits in the About dialog box.

control type, qwerty

Garstyciuks
July 29th, 2007, 14:59
It's nice that you found use from my emulator. But my name is Giedrius. :) I bet it's hard to pronounce it for english speakers... As well as my nickname. Most english people call me Garsty.

krypt100
July 29th, 2007, 20:03
Ha, sorry about that. I already fixed the spelling and re-uploaded my emulator.
and my native language isn't English either I bet it will be hard for me to pronounce any word not in my language :)

bronxbomber92
October 7th, 2007, 01:35
Hi,

I'm working on opcode Fx33. In the document I'm reading (http://devernay.free.fr/hacks/chip8/C8TECH10.HTM) it says

The interpreter takes the decimal value of Vx, and places the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2.

So, for example we have the hex number 0xFFFF (65535). My interpretation of the opcode would to store 5 (the hundreds digit) in memory[I], 3 (the tens digit) in memory[I+1] and 5 (the ones digit) in memory[I+2].

Is this what I should be doing? Or am I interpreting the doc wrong?

Here's my implementation in c++

case 0x33:
{
memory[I] = static_cast<char>((VX%1000)*.01f);
memory[I+1] = static_cast<char>((VX%100)*.1f);
memory[I+3] = static_cast<char>(VX%10);
break;
}

Doomulation
October 7th, 2007, 02:37
Doc says store the BCD representation of register X at I, I+1 and I+2. What BCD truly is, I don't know, unfortunately.

Garstyciuks
October 7th, 2007, 11:01
BCD = Binary Coded Decimal, which means that each digit of a decimal number should be seperated to several bytes.

bronxbomber92: I think that your implementation is wrong. First of all, you have memory[i] (lowercase i) in your 3rd line. And I don't really know if your code for determining the numbers works, but it can be done in a more efficient way. And what is VX? You should use the register which number is (opcode&0x0F00)>>8. Or was it only used as for an example? To get the digits, you have to do this:

third_num = num / 100; //assuming that num is not a float
second_num = (num % 100) / 10; //or second_num = (num / 10) % 10;
first_num = num % 10;

I don't remember in which exact order should the numbers be ordered in chip8 memory, there's only 2 possible ways to do that, so you should be able to figure it out easily.

bronxbomber92
October 8th, 2007, 02:07
Thanks!

VX is just

#define VX (reg[(instruction >> 8) & 0x0f])

bronxbomber92
October 9th, 2007, 20:25
All right, my Chip8 emu is pretty much finished (a few bugs left, but I don't feel like tracking them done :P).

What would be the next step from here? What would you guys suggest I try to emulate next?

d0u6
October 10th, 2007, 19:14
A Game Boy emu is a good candidate. Or maybe a more complex like a Nes, genesis, pcEngine, etc...

Doomulation
October 10th, 2007, 19:35
Gameboy is a pretty big step up from Chip8 though. It's much more complex.
There should be good systems in-between, but I don't know such, though I know there are some. At least one has been mentioned before.

pradipna
October 11th, 2007, 17:04
I am working in this chip 8 emulator project written in VB. I have made pretty much progress in past couple of days and games like blitz and space invaders are playable but I have some problems with font and stuffs. Here are some pictures:

http://i2.photobucket.com/albums/y6/pradipna/chip86.jpg

http://i2.photobucket.com/albums/y6/pradipna/chip87.jpg

http://i2.photobucket.com/albums/y6/pradipna/chip89.jpg

The problem of font not showing is caused by this instruction I guess: FX29 I points to the 4 x 5 font sprite of hex char in VX. I don't really know how to implement it.

d0u6
October 11th, 2007, 17:13
My Fr29 instruction



void font(BYTE Register) /* fr29 font vr*/
{
I = ((WORD) V[Register]) * 5;
}



And fonts are 8x5 bits, or 5bytes

Doomulation
October 11th, 2007, 17:14
I do not see how you cannot. They're bit-encoded. Basically, it's just data the the game points to then calls the draw sprite instruction.
As d0u6 mentioned, store the fonts somewhere in memory, and when the instruction is called, set the I register to the position where that data is stored.
I'm not sure if games can set the I register to font manually without using said instruction, but let's be careful and assume they can. Store font data at position 0 in memory and upwards.
TIP: You can also use Alt+Print Screen to take a snapshot of the current window.

pradipna
October 11th, 2007, 17:36
Good idea. Thanks...

pradipna
October 11th, 2007, 18:52
Ok, I added font in memory and it worked. This Cowgod's document was very helpful and everthing has been given in detail: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#8xy0

Here are some pictures after adding fonts:

http://i2.photobucket.com/albums/y6/pradipna/chip810.jpg
http://i2.photobucket.com/albums/y6/pradipna/chip811.jpg
http://i2.photobucket.com/albums/y6/pradipna/chip812.jpg

But in that first picture, it's suppose to be "Level 01". I dont know where the problem is.

Doomulation
October 11th, 2007, 19:09
If you're sure, then it should be a simple matter to find out why it isn't. Look for instructions that sets I to VX font or instructions that sets I to interpreter memory (< 0x200).

d0u6
October 11th, 2007, 19:38
did you do a disassembler? If yes, follow the game instruction and you will find the error. Put some printf in the instructions that use I to make the work easier..... Good luck.....

pradipna
October 12th, 2007, 03:05
My code for font FX29:


Dim mem(4096) as byte ' in general declaration

'in form load event

mem(0) = 240
mem(1) = 144
mem(2) = 144
mem(3) = 144
mem(4) = 240
.
.
.
.
mem(76) = 128
mem(77) = 240
mem(78) = 128
mem(79) = 128

'and then in Sub IPointsSpriteinVX()

addI = (v(opcode2) * 5)

I don't think there's is anything wrong with that code and works perfectly fine with other games except Blitz...

pradipna
October 12th, 2007, 16:57
Finally I found the problem. After going through the Cowgod's Refrence, I found my 'FX33 Store BCD representation of VX in M(I)...M(I+2)' instruction was wrongly implemented, so I re-wrote the code and it worked. Yayyyyyyy....
Here's some latest pictures:

http://i2.photobucket.com/albums/y6/pradipna/chip813.jpg
http://i2.photobucket.com/albums/y6/pradipna/chip814.jpg

I checked the compability and most chip 8 games are playable except the one named Hidden. In that game even if I open two wrong cards it says YEP! which should have been OPPS!. lol

d0u6
October 12th, 2007, 22:21
good work...... Congratulation


Begin now a GameBoy Emu........ Good Luck.......

pradipna
October 13th, 2007, 17:20
Thanks, I will code it in VB and I know it's gonna be slow. Any other simpler system than Game Boy to code in VB?

_Zack_
October 14th, 2007, 20:50
A space invaders emulator (see sticky)

pradipna
October 15th, 2007, 15:46
Hmmm... nice....

orphean
December 22nd, 2007, 05:33
This is a Chip-8 disassembler I wrote in C#. It requires .NET to be installed. Its based off of David Winter's UNCHIP but supports the few opcodes UNCHIP did not.

Here is an example of output:


; C8Dasm Version 1.0, Les Harris (orpheanjmp@gmail.com)

; Input File: WIPEOFF (206 bytes)

ld I, 2CC
ld VA, #07
ld V1, #00
0206:
ld VB, #08
ld V0, #00
020A:
drw V0, V1, 1
add V0, #08
add VB, #FF
se VB, #00
jp 20A
add V1, #04
add VA, #FF
se VA, #00
jp 206
ld V6, #00
ld V7, #10
ld I, 2CD
ld V0, #20
ld V1, #1E
drw V0, V1, 1
0228:
ld V3, #1D
ld V2, #3F
and V2, V0
add V7, #FF
sne V7, #00
jp 2AA
ld VF, K
0236:
ld I, 2CB
drw V2, V3, 1
ld V5, #FF
rnd V4, #01
se V4, #01
ld V4, #FF
0242:
ld I, 2CD
ld VC, #00
ld VE, #04
sknp VE
ld VC, #FF
ld VE, #06
sknp VE
ld VC, #01
drw V0, V1, 1
add V0, VC
drw V0, V1, 1
sne VF, #01
jp 298
sne V2, #00
ld V4, #01
sne V2, #3F
ld V4, #FF
sne V3, #00
ld V5, #01
sne V3, #1F
jp 2A4
ld I, 2CB
drw V2, V3, 1
add V2, V4
add V3, V5
drw V2, V3, 1
se VF, #01
jp 242
sne V3, #1E
jp 298
ld VA, #02
ld ST, VA
add V6, #01
sne V6, #70
jp 2AA
drw V2, V3, 1
rnd V4, #01
se V4, #01
ld V4, #FF
rnd V5, #01
se V5, #01
ld V5, #FF
jp 242
0298:
ld VA, #03
ld ST, VA
ld I, 2CB
drw V2, V3, 1
add V3, #FF
jp 236
02A4:
ld I, 2CB
drw V2, V3, 1
jp 228
02AA:
ld I, 2CD
drw V0, V1, 1
ld I, 2F0
bcd V6, [I]
ld V2, [I]
ld V3, #18
ld V4, #1B
ld F, V0
drw V3, V4, 5
add V3, #05
ld F, V1
drw V3, V4, 5
add V3, #05
ld F, V2
drw V3, V4, 5
02C8:
jp 2C8
dw #0180
02CC:
sne V4, #FF

Source is included.

I doubt anyone is that interested in this but I figured this is probably the only place someone might be interested at all so here it is!

Download: 37825

smcd
December 22nd, 2007, 05:45
Heh, that's about as far as I got for my emulator/simulator - built a core and disassembler, never implemented graphics. Maybe one of these days I'll finish :(

orphean
December 22nd, 2007, 06:00
I'm starting the emulator right now, then I figure I'll do an assembler just for fun. Make a Chip8 IDE haha.

Garstyciuks
December 23rd, 2007, 21:47
When I first was programming my emulator, I found it to be more fun to work without assembler/disassembler :).

orphean
January 1st, 2008, 03:41
Well I finished my Chip 8 Assembler. Working on speeding up my emulator right now, it works more or less but its amazing slow so I've done all my assembler testing on Hap's Fish n' chips emu.

The assembler has a built in chip8/schip code editor with most of the usual 'code editor' features:
http://grace.evergreen.edu/~harher07/c8asm.jpg

Its quite fast as you might imagine dealing with such a simple instruction set and tiny programs.

The program in the screenshot above is a simple C8 program I wrote that is included in the attached rar. It was assembled with the assembler and when running it looks like this:
http://grace.evergreen.edu/~harher07/bounce.jpg

The compiled program and the source for it is included in the attached rar. It requires .NET 2.0 to run.

I figure pretty much noone has been waiting for a new Chip8 assembler but I wrote it, so here it is!

Update: I updated the rar file after fixing some bugs in the assembler, also added support for the FX30 opcode (point I to the hi res font char in X) which wasn't in the main Winter's doc but is used in games and documented in the CHIPPER docs. There are some stupid UI bugs I'll quash soon and update again, but nothing that will keep someone from using it.



C8Asm Quick Docs

Note: These docs are not a substitute for 'real' chip8/schip docs. It does not cover particulars of when VF is set and why, etc.
Its main purpose is simply to list the mnemonics and syntax for the opcodes.

; starts a comment, comments are terminated at a newline.

All number literals are in hexadecimal and are preceded by # (ie, #FFFF)

Labels must be on their own line and terminate with a colon (:) (ie, label:)
Whitespace is mostly irrelevant except in special cases (like the required newline following a label declaration)

Opcode mnemonics are case insensitive.

The registers are as follows:
V# where # is 0-F - The 16 general purpose chip8 registers.
I - The memory pointer register.
[I] - Access value pointed at I (used for the reg->mem, mem->reg opcodes)
ST - Sound Timer
DT - Delay Timer
K - Key
F - Low Res Font
HF - High Res Font

Instructions:

high
Sets SCHIP graphics mode
Opcode: 00FF

low
sets CHIP8 graphics mode
Opcode: 00FE

cls
Clears the screen
Opcode: 00E0

exit
Quit program
Opcode: 00FD

sclr
Scroll 4 pixels right
Opcode: 00FB

scll
Scroll 4 pixels left
Opcode: 00FC

scld [literal]
Scroll [literal] pixels down. Ex: scld #4 will scroll down 4 pixels
Opcode: 00CK

dw [literal]
Echo [literal] into the bytestream. Ex: dw #FFFF will write #FFFF into the executable.
Opcode: None, Assembler directive.

call [label]
Calls a chip8/schip subroutine located at [label].
Opcode: 2NNN

jp [label]
Preforms an unconditional jump to [label]
Opcode: 1NNN

jp [label], [register]
Preforms an unconditional jump to [label] + contents of [register] Ex: jp main, V0
Opcode: BNNN

ret
Return from a chip8/schip subroutine.
Opcode: 00EE

drw [register1], [register2], [literal]
Draws a sprite 8x[literal] lines at [reg1],[reg2]. If [literal] is 0 it draws a 16x16 sprite. Ex: drw V0, V1, #3
Opcode: DXYK

se [register], [literal]
Skip next line iff [register] == [literal]
Opcode: 3XKK

se [reg1], [reg2]
Skip next line iff [reg1] == [reg2]
Opcode: 5XY0

sne [register], [literal]
Skip next line iff [register] != [literal]
Opcode: 4XKK

sne [reg1], [reg2]
Skip next line iff [reg1] != [reg2]
Opcode: 9XY0

skp [register]
Skip next line iff the key in [register] is pressed.
Opcode: EX9E

sknp [register]
Skip next line iff the key in [register] is not pressed.
Opcode: EXA1

rnd [register], [literal]
Stores a random number in [register] with the range 0-[literal]
Opcode: CXKK

add [register], [literal]
Adds [literal] to [register]
Opcode: 7XKK

add [reg1], [reg2]
Adds [reg2] to [reg1]
Opcode: 8XY4

add I, [register]
Adds [register] to I.
Opcode: FX1E

sub [reg1], [reg2]
Subtracts [reg2] from [reg1]
Opcode: 8XY5

subn [reg1], [reg2]
[reg1] = [reg2] - [reg1]
Opcode: 8XY7

or [reg1], [reg2]
[reg1] = [reg1] | [reg2]
Opcode: 8XY1

and [reg1], [reg2]
[reg1] = [reg1] & [reg2]
Opcode: 8XY2

xor [reg1], [reg2]
[reg1] = [reg1] ^ [reg2]
Opcode: 8XY3

shl [register]
[register] << 1
Opcode: 8X0E

shr [register]
[register] >> 1
Opcode: 8X06

bcd [register]
Stores the BCD representation of M(I)..M(I+2) in [register]
Opcode: FX33

hps [register]
Store registers V0 to [register] in HP Flags where [register] < 8
Opcode: FX75

hpl [register]
Load registers V0 to [register] from HP Flags where [register] < 8
Upcode: FX85

ld [register], [literal]
Loads [literal] into [register]
Opcode: 6XKK

ld [reg1], [reg2]
Loads [reg2] into [reg1]
Opcode: 8XY0

ld I, [label]
Loads the address of [label] into I.
Opcode: ANNN

ld F, [register]
Points I to the lowres font char stored in [register]
Opcode: FX29

ld HF, [register]
Points I to the hires font char stored in [register]
Opcode: FX30

ld [I], [register]
Saves registers V0 to [register] in memory starting at location I.
Opcode: FX55

ld [register], [I]
Loads registers V0 to [register] from memory starting at location I.
Opcode: FX65

ld [register], DT
Stores the contents of DT into [register]
Opcode: FX07

ld DT, [register]
Stores [register] into DT
Opcode: FX15

ld ST, [register]
Stores [register] into ST
Opcode: FX18

ld [register], K
Waits for keypress and stores result in [register]
Opcode: FX0A

deltaphc
January 5th, 2008, 22:48
Hello everyone.

After reading through most of this thread and consulting two or three Chip-8 docs, I've made yet another Chip-8/SChip emulator!

http://delta.slinked.net/files/ChipSharp/ChipSharp-100beta1.zip
Called ChipSharp. Programmed using C#, .NET 2.0, and SdlDotNet (http://cs-sdl.sourceforge.net/index.php/Main_Page).

Some features:

Speed control
Screenshot function
2x/4x/8x zoom
Adjustable colors
Adjustable beep
CPU runs most programs. A few still have problems, like hap's emutest
Download includes hap's Chip-8 program collection
Uses the 1234QWERASDFZXCV key layout


It's in beta mostly 'cause there's more I want to add, like input remapping, saving user preferences, and compatibility options. As for source code, I'll release the emulation core a bit later; still doing tweaking, adding comments, etc.

Development started on January 2nd. I'm surprised it turned out as well as it did; this is my first emulator. It could probably use a little optimization, but it appears to run fine in most cases.

Screenshots:
http://img177.imageshack.us/img177/3928/chipsharpcarfj0.th.png (http://img177.imageshack.us/my.php?image=chipsharpcarfj0.png)
http://img244.imageshack.us/img244/7003/chipsharpantog2.th.png (http://img244.imageshack.us/my.php?image=chipsharpantog2.png)
http://img184.imageshack.us/img184/4498/chipsharpjoustkb7.th.png (http://img184.imageshack.us/my.php?image=chipsharpjoustkb7.png)
http://img296.imageshack.us/img296/4200/chipsharprushhourls0.th.png (http://img296.imageshack.us/my.php?image=chipsharprushhourls0.png)

Zieg
February 1st, 2008, 16:06
Hi guys, I'm starting a Chip-8 emulator too.
But there's something I don't understand...
In pong, for example, there's the opcode 22D4, so it should run a subroutine at the address 724.
But the Pong ROM is only 246 bytes long....
How can I run the subroutine at 724 if that address doesn't exist?
Someone explain please ^^

Garstyciuks
February 1st, 2008, 22:05
The first 512 bytes of memory is reserved for the interpreter. It used to contain the interpreter code for some processors, so they could execute the chip8 code. You may use it anyway you like it. I personally use that space to store the font data in my emulator. And you have to load the ROM starting from memory address 0x200 (or 512 in decimal).

deltaphc
February 1st, 2008, 23:47
http://delta.slinked.net/files/ChipSharp/ChipSharp-100beta2.zip

ChipSharp v1.00 beta 2 released! Now includes full source code, under a BSD license.

Changes since beta 1:

Now compiled using C# 2008/VS2008. Still only requires .NET 2.0, thanks to multitargeting.
In Chip8 mode, 4 pixels are now drawn internally on a 128x64 display. Before, it would skip every other pixel and depend on the video output routine to draw 4 pixels to the final bitmap.
Scrolling is more accurate. Scroll Test and Emutest both produce better output.
Fixed a subtraction and left shift opcode; Sokoban is now playable.
[video output] Using GDI+ instead of SdlDotNet's SurfaceControl.
[video output] Significant speedup.
[video output] Picture is no longer blurry.
[video output] Performs well regardless of how many pixels are set.
Added input remapping. Available in Options-->Input-->Configure.
Added shortcut keys for various functions. Look in the menus for details.
Added option for disabling sprite wrapping.
Fixed a crash when the beep sound would run out of channels to play on.
Fixed a crash when the user closes the emulator while having a program running.
Several DLLs were deleted, since they are no longer needed.
Miscellaneous tweaks and polish.

Zieg
February 2nd, 2008, 00:38
The first 512 bytes of memory is reserved for the interpreter. It used to contain the interpreter code for some processors, so they could execute the chip8 code. You may use it anyway you like it. I personally use that space to store the font data in my emulator. And you have to load the ROM starting from memory address 0x200 (or 512 in decimal).

Got it, thanks!

Memery
March 29th, 2008, 12:43
I recall somebody mentioning that a particular emulator was able to detect when some ROMs were in "Game Over" state. I've noticed most ROMs will enter an infinite loop to signify the end of gameplay - this can be handled by checking if a jmp destination is equal to its own address in memory (which would cause an infinite loop) and breaking execution with a game over notification of some sort.

How do you all deal with display refreshes? I send off the video RAM for processing whenever a sprite or cls opcode is executed. I'm thinking of periodically updating the display at some fixed time period (Eg. 60Hz) rather than hammering away whenever a long sequence of sprite opcodes are executed... What was your strategy?

Sprite wrapping seems inconsistent between ROMs. BLITZ will not execute correct if vertical wrapping is enabled, but it seems that almost every other ROM expects wrapping along both X and Y axes. Is this an error of the programmer, or David Winter's documentation?

hap
March 29th, 2008, 13:24
If you tell your OS to update the display every time the CHIP-8 graphics are updated, it might cause slowdown. Just write to a secondary VRAM buffer for graphics updates, and yes, update the OS display at a fixed interval like 60Hz.

Sprite wrapping is an emulator enhancement. I don't think any games rely on it, eg. Brix doesn't work properly with it either: if the bat is halfway off the screen, the part on the left side won't deflect the ball.

Marce1991
April 24th, 2008, 01:38
Hey guys!
I have alot of questions here(about 8 chip), I was wondering if anyone could help me O.O
Well here it goes:
1-People say 'NN' (part of an opcode) is a constant. Is it an integer?What's its value?
2-I'm currently doing the emulation the worst way I suppose, here goes a sketch on pseudo coding:
read rom
search memory for opcodes
if opcode1
doopcode1();
if opcode2
doopcode2();
...
What's the best way of doing this?
3-I have ran few checks, and it seems that 'pong' does not have some of the opcodes ( for instance 00EE). Is that normal?
4-How do I declare/build an array containing many strings?I'd at least like to search for the opcodes by index O.O
e.g.: (this is the mem search function)
I'd like to do this
search = (char*) memchr(memblock, *op[n] , size);
but currently im more onto this:
search = (char*) memchr(memblock, *op0 , size);
search = (char*) memchr(memblock, *op1 , size);
search = (char*) memchr(memblock, *op2 , size);
...

(lol)
5- What are those flags (VF-VX). Are they adresses? Bool variables?Im kinda confused.
6-What's the best way of rendering?Go straight for directX, windows or maybe can the c++ language handle monochromatic rendering?haha
7-The 'sprites' the opcodes refer to are actually white spots right?
Thanks in advance!And sorry for the excessive questioning...
Cheers~
edit:sorry almost forgot...
8-How do I deal with the hertz cycle, ppl say it counts down to zero at 60 hertz.But 60 hertz is a frequency. So from what value do I start? And how is the interruption?How long does it last?
9- How do I handle the 'carry' thing and the 'borrow'. What are them for?
Thanks again!

Garstyciuks
April 25th, 2008, 15:27
Here goes:

1. All opcodes are 2 bytes size. For example, if an opcode is 1NNNN (the jmp opcode), then you get the NNN number by doing (opcode&0x0FFF).

2. The easiest way to emulate something is to directly interpret all of its opcodes. Here's an example of a possible execution function:


void execute()
{
unsigned short opcode = (memory[pc]<<8) | memory[pc+1];
switch((opcode&0xF000)>>24)
{
case 0x0:
if ((opcode&0x00FF) == 0xE0)
{
//clear screen
}
else if ((opcode&0x00FF) == 0xEE)
{
//return from a subroutine
}
pc += 2;
break;

case 0x1:
pc = opcode&0x0FFF;
break;

case ...:
...
}
}

3. Each ROM use the opcodes they need to use.

4. You aren't supposed to search for the opcodes. You simply load the ROM starting at memory address 512 and start the execution from there, not caring what value is located at the program counter (pc). If the ROM doesn't contain any errors, then your emulator will work fine.

5. V0 - VF aren't flags. They are CPU registers, there are 16 of them and they can store 1 byte (unsigned char) values. They are used for remembering the x and y locations of objects, etc...

6. There is no best way of rendering. It really depends on your preference. For example, I used OpenGL on the Windows version of my emulator and SDL on the cross-platform version.

7. The DRAW opcode requires the I (address) register to point to the pixel data. The width of sprites in basic chip8 mode is fixed to 8 and the height is specified in the opcode's last nibble. Each byte represents one line of the sprite, one bit represents one pixel (1 for white, 0 for black).

8. The delay and sound counters initially are set to 0. You only need to decrement them if they are larger than 0. If the delay counter is set, then you need to stop the execution of other opcodes, until the counter is 0, and if the sound counter is set, you continue execution, but play a beeping sound as long as the sound counter is larger than 0. And you should decrement them at 60Hz interval.

9. The carry and borrow flags are generated by arithmetic opcodes and are saved in VF register (16th register) as 1 (if carry/borrow occurred) or 0, if it didn't. The carry occurs when you add two values and the result exceeds the number of bits. For example if you would add 0x10 and 0xFF, the result would be 0x09 (since all the registers are 8bit sized) and a carry flag would be generated. The borrow flag is pretty similar to carry flag and it is set when the first operand of subtraction is smaller than the second one.


Hope I made some things clearer. Good luck on your programming!

Doomulation
April 30th, 2008, 23:16
8-How do I deal with the hertz cycle, ppl say it counts down to zero at 60 hertz.But 60 hertz is a frequency. So from what value do I start? And how is the interruption?How long does it last?
It means it should execute 60 cycles per second. Timing isn't very good for chip8, so typically they just mean that you should execute 60 opcodes, check the registers and whatnot, all 60 times per second.

CodeSlinger
May 21st, 2008, 08:02
Running the emulator at 60 opcodes per second I find is dead slow. Games like wipeoff and invaders are just too tedius to play.

Trouble is that ive never played the original chip8 or played anyone elses emulator to know what the correct speed of the game should be.

I find running it at 400 opcodes per second is the most enjoyable.

Doomulation
May 22nd, 2008, 11:21
This, is unfortunately a problem. Some games run faster than others. Some require a lot of cycles / second, some requires few. There's no way around it, unfortunately.
Timing in chip8 is not known very much.

CodeSlinger
May 22nd, 2008, 13:03
Thanks for the reply.

I guess I'll leave timing as is for the moment then. Sound seems to work fine no matter how i deal with the timing so I guess the best way forward is to perhaps have the emulator ini file configurable for how many opcodes per second it should exectue. That way the player can adjust it themselves. Not that anyone's ever gonna play on it, but still worth doing.

I'll iron out a few more bugs like why some of the graphics flicker on and off and then i'll start the gameboy emulator.

Garstyciuks
May 22nd, 2008, 20:53
The graphics flicker because it's the way chip8 is designed. It moves an object by drawing the same image data on top of where the object originally was and then draws the sprite at a different location. If the screen gets updated in the middle of this, the graphics flicker.

I don't see any reliable and well designed solution for this. Though there could be some hacks made, like not updating the screen until the sprite is moved, but there are certain situations which would destroy the balance of this method.

Doomulation
May 22nd, 2008, 21:49
Yes, it would be more like hacks and can break things.
It is unfortunately the way Chip8 was designed. It's a weak system, after all.

CodeSlinger
May 23rd, 2008, 07:57
I see. I dont mind the graphics flickering if they are supposed to flicker because that means im doing something right. To stop the graphics flickering would mean hacking the original system which I guess is not the point in emulation.

When i get a chance i'll track down the other bugs in the emu, but it's sounding like im fairly close to having it working.

Thanks guys.

J0hn
September 25th, 2008, 18:04
This is my emulator, there's also a simple schip/chip8 disassembler in pseudo c code and the rom of the game blitz whitout the "instant game over" bug.

Link (http://www.box.net/shared/450kb17g2k)

Thesuperchang
November 24th, 2008, 05:10
Hi, I am the one known as Thesuperchang. Recently I've been working on a chip8 emulator. At the moment I am only missing the Super, sound, input and graphics opcodes so the emulator should emulate as specified.

I have developed my emulator in a Linux environment so its likely that I'll use the X Library for all my graphics and user input. As for the sound, I'm still not sure.

The source to my emulator can be found below:
http://www.megaupload.com/?d=JQRZ1P42

Any constructive criticism would be greatly appreciated.

---

I've reworked a few things in the emulator above. Quite a few bugs have been located and fixed. Graphics has been implemented, however there are gfx bugs. This is likely due to by drawing algorithm as all graphics are rotated 90 degrees and some overlap.

Most recent source can be downloaded below;
http://www.megaupload.com/?d=BPYJPB35

Regards,
-Thesuperchang

Aire83
January 3rd, 2009, 14:56
i've coded all the opcodes , now im need to somehow display the graphic onto the screen, which i'm totally lost. Can somone help me get started?

so i have a multi-d array for the video memory that contains bool values. I don't get how to use these values to draw the sprites onto the screen.

I've looked through all the posts in this thread and didnt found anything helpful on this matter

Doomulation
January 3rd, 2009, 15:12
It's quite simple. Each element in the array represents a pixel on the screen. If it's true, then you XOR the pixel in that location (meaning, if there is no pixel at that position, then you write it; and if there is a pixel on that location, you erase it).
If it's false, you do nothing.


bool Memory[0xFF][0xFF]; // Dummy values!
for (int i = 0; i < 0xFF; i++)
{
for (int j = 0; j < 0xFF; j++)
{
if (Memory[i][i])
XorPixel(i, j);
}
}

Thesuperchang
January 4th, 2009, 10:40
I've located the source of quite a few bugs within my emulator, however graphics is cactus. Below is my DXYN (drawing) operation code. For the life of me I cannot figure out what is going wrong with it now.

http://img296.imageshack.us/img296/2773/chip8pic5.th.png (http://img296.imageshack.us/my.php?image=chip8pic5.png)http://img296.imageshack.us/img296/6703/chip8pic4.th.png (http://img296.imageshack.us/my.php?image=chip8pic4.png)http://img296.imageshack.us/img296/7049/chip8pic2.th.png (http://img296.imageshack.us/my.php?image=chip8pic2.png)http://img296.imageshack.us/img296/7624/chip8pic3.th.png (http://img296.imageshack.us/my.php?image=chip8pic3.png)http://img296.imageshack.us/img296/9329/chip8pic1xh8.th.png (http://img296.imageshack.us/my.php?image=chip8pic1xh8.png)http://img186.imageshack.us/img186/6020/chip8pic0or3.th.png (http://img186.imageshack.us/my.php?image=chip8pic0or3.png)


void DrawSpriteXbyN(unsigned short int opCode, bool **screen, unsigned char *gReg, unsigned short int *aReg, unsigned char *memory, _Window *window) {
// DXYN
int i, n;

gReg[0x0F] = 0x00;
for(n = 0; n < (opCode & 0x000F); n++) {
for(i = 0; i < 8; i++) {
if( memory[*aReg + n] & (0x80 >> i) ) {
screen[gReg[(opCode & 0x00F0) >> 4] + n][gReg[(opCode & 0x0F00) >> 8] + i] =
!screen[gReg[(opCode & 0x00F0) >> 4] + n][gReg[(opCode & 0x0F00) >> 8] + i];
if(screen[gReg[(opCode & 0x00F0) >> 4] + n][gReg[(opCode & 0x0F00) >> 8] + i] == true) {
XFillRectangle(window->display, window->window, window->colour[0]->gc, ((gReg[(opCode & 0x0F00) >> 8] + i) * RESOLUTION), ((gReg[(opCode & 0x00F0) >> 4] + n) * RESOLUTION), RESOLUTION, RESOLUTION);// Draw pixel
}
else {
XClearArea(window->display, window->window, ((gReg[(opCode & 0x0F00) >> 8] + i) * RESOLUTION), ((gReg[(opCode & 0x00F0) >> 4] + n) * RESOLUTION), RESOLUTION, RESOLUTION, 0);// Erase Pixel
gReg[0x0F] = 0x01;
}
}
}
}
// *aReg += n;

return;
}

Any suggestions would be greatly appreciated.

Regards,
-Thesuperchang

Doomulation
January 4th, 2009, 11:55
Use code tags instead of quotes. The code is unreadable.

Aire83
January 5th, 2009, 15:09
Hey Doom. Thanks for replying, but i still don't it. lets say videoMem[0x3a, 0x3c] is true. how do i tell the api to draw the pixel. Do i choose the dimension of the pixel and draw the pixel at (0x3a, 0x3a)?

Thesuperchang
January 6th, 2009, 02:54
I slowed my emulator down to one operation per second. The graphics were mostly fine, with exception to the aliens in INVADERS. My address pointer is probably getting screwed up somewhere. As far as I'm concerned, it's good enough to brush under the bed and start a new project.

The source to my Chip-8 emulator can be found below;
http://www.megaupload.com/?d=ZGEE89D4 (Download)

The source also contains a very basic graphics library that I made. It calls functions from the X Window System and hides all the verbose crap from the nice looking source. It should work under any system that supports the X Window System.

Aire83
January 6th, 2009, 08:18
k i got the pictures to display on the screen, but how do i make the pictures to fix on the screen? i tried to multiply the position of the pixel to be drawn by 4 cause my window is 4 times bigger but that didnt seem to work

can someone help me?

Thesuperchang
January 6th, 2009, 10:01
My method was to define a constant called RESOLUTION which would act as a magnification multiplier. To draw sprites, rather then doing a draw pixel command, I used a fill rectangle command. The origin of the rectangle multiplied by the constant RESOLUTION as is the width and height. This method is displayed above in an earlier post I made.

Aire83
January 7th, 2009, 04:49
Can someone tell me what's wrong with my draw method?


DrawSprite( byte rVX, byte rVY, byte opcode )
{
registers[0xF] = 0;


int pixelWidth = 8;
byte pixelData;

int xSprite = rVX ;
int ySprite = rVY ;

int pixelHeight = ( opcode & 0x000F );
//if (!chip8)
//{ pixelHeight = 16; }

if (pixelHeight == 0) pixelHeight = 16;


for (int y = 0; y < pixelHeight; y++)
{
pixelData = memory[I + y]; //retrive the byte for pixels

for (int x = 0; x < pixelWidth; x++)
{

if (( pixelData & (0x80 >> x)) != 0)
{
int row = (xSprite + x) ;
int column = (ySprite + y) ;

if (videoMemory[row, column] == true)
{ registers[0xF] = 1; }
videoMemory[row, column] ^= true;
}
}
}
}

Thesuperchang
January 7th, 2009, 06:01
Maybe this.


DrawSprite( byte rVX, byte rVY, byte opcode )
{
registers[0xF] = 0;


int pixelWidth = 8;
byte pixelData;

int xSprite = rVX ;
int ySprite = rVY ;

int pixelHeight = ( opcode & 0x000F );
//if (!chip8)
//{ pixelHeight = 16; }

if (pixelHeight == 0) pixelHeight = 16;


for (int y = 0; y < pixelHeight; y++)
{
pixelData = memory[I + y]; //retrive the byte for pixels

for (int x = 0; x < pixelWidth; x++)
{

if (( pixelData & (0x80 >> x)) != 0)
{
int row = (xSprite + x) ;
int column = (ySprite + y) ;
videoMemory[row, column] = !videoMemory[row, column];
}
}
}
}

The boolean data type can either hold a state of true or false. From my experience on the windows platform the bool can be defined as below;

typedef char bool;
#define true 0x01
#define false 0x00
In ansi C, true is defined as not 0. Alright now that we have that out of the way, the problem with your code is that "^" is a bitwise operation. Therefore the result of your array will always retain the value false. eg.


uint8 varA;
varA = 0x01;
for(;;)
{
varA = varA ^ varA; // varA evaluate as 0x00 forever.
}


I hope that helps.

Aire83
January 7th, 2009, 07:11
hmm.. i thought the way chip8 draws the sprites is by using XOR

Thesuperchang
January 7th, 2009, 07:46
hmm.. i thought the way chip8 draws the sprites is by using XOR

It does. Reference to the XOR truth tables below. When we attempt to activate a point on the screen with chip-8 we XOR it.
Let input A be the current state of the point.
Let input B be a constant 1.
Given that we know B is always a constant, the result in rows 0 and 3 can be ignored. Looking at rows 1 and 2 we can see the boolean equation is 0 == !A


| A | B || O |
|------------|
| 0 | 0 || 0 | - Row 0
| 0 | 1 || 1 | - Row 1
| 1 | 1 || 0 | - Row 2
| 1 | 0 || 1 | - Row 3

Doomulation
January 7th, 2009, 11:30
How, exactly, to draw something to the screen is very API-specific. Firstly, you would have to mention what API you are using.
Secondly, C++ defines bool as a datatype which can contain true or false; C does not define such a type. However, usually C++ is used to create emulators and games. This, simply because, C++ is an evolution of C - it maintains backwards compatibility, speed, flexibility, as well as adds more modern features to make the projects more maintainable.

I would also stress that an emulator should not have any bugs, such as corrupting pointers.
Emulating everything and correctly is another matter.

dreampeppers99
January 8th, 2009, 17:14
I'm having problems with my Schip8 "emulator".
The chip8 games runs fine however the schip8 runs but the "screen" is presented some strange cuts ... look the "pac man" and "ant"

http://www.uploadimagens.com/upload/b7f2934fbe332f4c47090c75ea5b77d1.jpg

My ant game doesn't show the "land"...

http://www.uploadimagens.com/upload/999e972364f46a9f2384220ed9c4a5e0.jpg

The dragon2 seems to have some issues too...

The I thought this could be a Scroll stuff and I made this test below but the results seems to be correct.


Processor cpu = new Processor();
int initialAddress = 0x200;

Engine videoMock =new Engine(cpu);
Emulator.setVideo(videoMock);
Emulator.init();

cpu.dataRegister.V[1] = 0x0; //x
cpu.dataRegister.V[2] = 0x0; //y
cpu.addressRegisterI = 0x20A; //initial sprit 16x16

cpu.getMemory().writeAt(initialAddress++ , (short)0x00); //setting schip mode
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF);
cpu.getMemory().writeAt(initialAddress++ , (short)0xD1); //draw 1 2 16x16 from 0x20A
cpu.getMemory().writeAt(initialAddress++ , (short)0x20);
cpu.getMemory().writeAt(initialAddress++ , (short)0x00); //scroll down 1
cpu.getMemory().writeAt(initialAddress++ , (short)0xC1);
cpu.getMemory().writeAt(initialAddress++ , (short)0x00); //scroll 4 to rigth
cpu.getMemory().writeAt(initialAddress++ , (short)0xFB);
cpu.getMemory().writeAt(initialAddress++ , (short)0x00); //scroll 4 to left
cpu.getMemory().writeAt(initialAddress++ , (short)0xFC);
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111
cpu.getMemory().writeAt(initialAddress++ , (short)0x80); //10000000
cpu.getMemory().writeAt(initialAddress++ , (short)0xFF); //11111111

cpu.step(); //set high
cpu.step(); //false draw, just put the 1s and 0s on matrix
print(videoMock.getFrame()); // take a snapshot, 1

cpu.step(); //scroll down 1
print(videoMock.getFrame()); // take a snapshot, 2

cpu.step(); //scroll 4 or 2 for rigth
print(videoMock.getFrame()); // take a snapshot, 3

cpu.step(); //scroll 4 or 2 for left
print(videoMock.getFrame()); // take a snapshot, 4

Snapshot 1 (just draw)
======================================== =======================================
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|

Snapshot 2 (scroll down 1)
======================================== =======================================
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|

Snapshot 3 (scroll rigth 4/2)
======================================== =======================================
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|

Snapshot 4 (scroll left 4/2)
======================================== =======================================
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
1|0|0|0|0|0|0|0|1|1|1|1|1|1|1|1|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 0|0|0|0|0|0|0|0|0|0|0|0|0|


Im my "loggin" system shows...
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=31
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=32
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=30
....
So I think it can be a error of my part make the boundary over 131 (I made 0-127 ==[2*64] ).
You guys mark pixel on this coordinate? (at x=132)

I notice too the ant... starts with:
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=57
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=133,y=58


Debugger says me:

[CPU] DEBUG: high
[CPU] DEBUG: old drw 4
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=55
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=57
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=133,y=58
[CPU] DEBUG: scl
[CPU] DEBUG: old drw 4
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=55
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=57
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=133,y=58
[CPU] DEBUG: scl
[CPU] DEBUG: old drw 4
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=55
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=132,y=57
[VideoEngine] ERROR: Pixel[1] --> it couldn't be happen' x=133,y=58
[CPU] DEBUG: scl

The game plot and give the scroll left 4 pixels...

But why if the resolution is 128x64?

I just solve this changing:
//private int widhtSchip = 128, heigthSchip = 64;
private int widhtSchip = 148, heigthSchip = 84;
The question is why I must have an array bigger than the resolution?

Best regards,

MicBeaudoin
January 12th, 2009, 06:48
Hello, my name is Michael and this is my first post, but I've been looking around for a long time and learnt a lot from these forums.

So I did the Chip-8 emulator, like everyone else here. It was a fun project. Getting a result from emulator programming takes a lot of time, but it's incredibly rewarding once it's done.

My emulator is very basic and is called 'ChipLol'. I made a zip file with a lot of Chip8 games so you can (re)try them now. It's in french(I'm from Montreal and my primary language is french), but you shouldn't have much trouble finding how to open a rom knowing there is only one useful menu item in the window.

Before leaving, I read somewhere that it's faster to make a texture from an array of pixel and display it with one GL_QUADS. I don't know much about OpenGl(only basic polygon creation and color), but I think my method is VERY slow :

For each Chip8 pixel:
If(ON)
Draw a rectangle
EndIf
End for

I just don't know how to create a texture from an array of data so it will wait.

I decided not to post the source because it is very messy right now.

I couldn't upload the file here(the forum message says:"unable to move/copy file") so I put it on Megaupload for now:

http://wikisend.com/download/884570/ChipLol.zip
http://www.megaupload.com/fr/?d=UQD4XDRT

dreampeppers99
January 14th, 2009, 16:33
For a while I'll release my chip8 schip8 implementation and source code too... I hope it can help anyone that are looking for info to chip8 schip8....
The language was Java and the render engine was Java2D (you can not believe, but it can be slow if you are a bad coder as I'm). So it is the Chip8 Emulator made with Java.

The chip8 games running under JChip8BR
http://www.uploadimagens.com/upload/6f20751bfa1fc6f57a2c203ad49ae4f3.jpg

The schip8 games running under JChip8BR
http://www.uploadimagens.com/upload/4e5e91be7eaebdec196b6e4c6fe3d064.jpg

Disassember in two ways
http://www.uploadimagens.com/upload/7e6ed6508400a0927757d9f90599a6ce.jpg

The memory viewer
http://www.uploadimagens.com/upload/9d71c90d777b02853d9459b5bef75a28.jpg

You can debug step by step and see the registers values thus like the Screen. (pong is doing that here)
http://www.uploadimagens.com/upload/be62ff0adad6514466c65073c4cbfae2.jpg

The configs
http://www.uploadimagens.com/upload/06b3b484e78b41d6aff64569bd50be8b.jpg

The source and emulator link
http://www.2shared.com/file/4611492/b4e7c445/jchip8br.html

CodeSlinger
January 15th, 2009, 07:40
Good work on your debugger mate, looks pretty slick.

I always intended to write a debugger for my sega master system emulator but never got round to it. I'll have to write one for the sega mega drive/genesis before I start emulating that system as I dont think I'll get far without one!

dreampeppers99
January 16th, 2009, 15:23
Good work on your debugger mate, looks pretty slick.

I always intended to write a debugger for my sega master system emulator but never got round to it. I'll have to write one for the sega mega drive/genesis before I start emulating that system as I dont think I'll get far without one!
Thanks!
I think write a debugger can help fix and find "bugs" (besides to put more challenges on project)

dreampeppers99
January 17th, 2009, 19:41
The source code from JChip8BR is on svn google's.
http://code.google.com/p/jchip8br/
The code is not so beautiful and faster but I hope you can take some acknoledges.

Death the Kid
February 18th, 2009, 23:32
I have coded all my opcodes i need for my chip8 emulator and I created a started coding the graphics with directx, but tbh i dont know how to tie the two together from there on, any point in the right direction would be greatly appreciated. :batman:

Doomulation
February 19th, 2009, 08:40
The last page contains a lot of discussion regarding the graphics. I have no idea what you are looking for.

Death the Kid
February 19th, 2009, 10:06
well am looking for some help with using directx for my emulator, since so far i only have figured out how to intialize a window lol (sorry about not being more specific) any kind of help would be greatly appreciated.

h4tred
February 21st, 2009, 02:17
Oh alright.

Well, first you need to get a pointer/reference to the graphics output of your emulator. Often this is like a buffer which stores the image.

From there, there is heaps of ways to do it. You can use DirectDraw to draw the raw output, blit to a texture in D3D or OpenGL, or just blit to surfaces in SDL.

One site I recommend is Codesampler.com, there contains some D3D and OpenGL tuts. I just blitted to a OpenGL texture for my NES emu using glTexSubImage2D and altering the texture coordinates to scale/skew the image.

Death the Kid
February 21st, 2009, 10:57
so it is possible to do this emulator with just SDL, cause I got the impression that you couldn't, well that helps alot thanks man

Death the Kid
February 23rd, 2009, 19:05
uhhh am having a problem with my emualtor, i pretty much coded the entire thing but, there is no screen output, am pretty sure this is because the ROM isnt being loaded in properly. I have used a break point to find the problem



bool Emu::Load()
{
CPUReset();
ClearScreen();

FILE* in;
in = fopen("C:/"Pong", "rb") ;


if (0 == in)
{
return false;
}

fread(&m_GameMemory[0x200], ROMSIZE, 1, in) ;
fclose(in);

return true;

}



the in pointer has this
error CXX0030: Error: expression cannot be evaluated

h4tred
February 24th, 2009, 00:14
Problem in fopen(), you did it wrong.

Try "pong.rom" or something, or change your loading function like so:

Emu::Load(char* file)

........
in = fopen(file, "rb") ;

Exophase
February 24th, 2009, 00:52
uhhh am having a problem with my emualtor, i pretty much coded the entire thing but, there is no screen output, am pretty sure this is because the ROM isnt being loaded in properly. I have used a break point to find the problem



bool Emu::Load()
{
CPUReset();
ClearScreen();

FILE* in;
in = fopen("C:/"Pong", "rb") ;


if (0 == in)
{
return false;
}

fread(&m_GameMemory[0x200], ROMSIZE, 1, in) ;
fclose(in);

return true;

}



the in pointer has this
error CXX0030: Error: expression cannot be evaluated

The problem is that you have a stray " in the middle of your string - if you really meant to open a file under C:/ called "Pong (I have my doubts) then you need to escape the quote like this: "C:/\"Pong".

Are you sure you coded pretty much all of this, because this is a pretty obvious error, and the inconsistent indentation is a little telling...

Furthermore, you wouldn't have used a breakpoint to find such an error, because barring some rather freak circumstances this shouldn't even compile.

Or maybe you pasted the code incorrectly?

Death the Kid
February 24th, 2009, 07:47
oh yeah that sorry i didnt paste in in properly that line is suppose to be



in = fopen("Pong.ch8", "rb");

in = fopen("Pong", "rb");


i tried both in my program with the same results

Exophase
February 24th, 2009, 16:41
Do you actually have a file named Pong.ch8 in the same directory as your executable? Where are you putting the breakpoint to check for in? What is the value of ROMSIZE?

Death the Kid
February 24th, 2009, 17:31
yeah i do have a ROM called Pong there, the I placed the breakpoint at the fclose line and below is the code for ROMSIZE and m_GameMemory



typedef unsigned char BYTE;

const int ROMSIZE = 0xFFF;

BYTE m_GameMemory[ROMSIZE];

Exophase
February 24th, 2009, 20:14
If it made it that far then it probably loaded the ROM and your error(s) can be just about anywhere else.

To debug this you really need to write a debugger that allows you to step through each instruction and view registers and memory to make sure that they're all doing what they're supposed to be doing. Having breakpoints doesn't hurt either.

Death the Kid
February 24th, 2009, 22:00
so the error below i found in the in pointer isnt that significant then ? (just want to make absolutely sure LoL), plus when i checked the game memory to see how much of the ROM was loaded about it was about six bytes short

error CXX0030: Error: expression cannot be evaluated

Exophase
February 24th, 2009, 22:07
The "error" in question is probably just that you're trying to display the value of a FILE and the debugger doesn't know how. Although why said debugger would try to display what it's pointing to instead of the pointer itself (which is usually more useful, except for maybe char *) is beyond me.

Death the Kid
February 24th, 2009, 23:07
LOL cool then, just one more thing could you please point me in the right direction with creating a chip 8 debugger cause tbh i dont have a clue where to start.

Exophase
February 25th, 2009, 14:58
A good place to start is to have it so before executing each instruction you print out which instruction you're about to execute and the value of all the registers, then wait for the user to press a key. Then from here you can take in commands to do other things like inspect regions of memory, set breakpoints, exit single step debugging, etc. A breakpoint would reenter this kind of debugging mode when the PC is at the breakpoint value. You can also disassemble instructions so that you know what you're executing without having to look it up all the time.

h4tred
March 1st, 2009, 04:57
Are you sure you coded pretty much all of this, because this is a pretty obvious error, and the inconsistent indentation is a little telling...

Yes, its a blatant coderip from CodeSlinger's Chip8 emulator.

sammyboy
March 1st, 2009, 20:05
omg, this thread went on for epic.

I'm famous.

http://www.vgchartz.com/avatars/5677751ccc.png

Death the Kid
March 2nd, 2009, 17:51
Yeah i followed codeslingers site on how to write an emulator, but even so i entered that qoute Mark after I had pasted it in here because i thought i left it off when i copied it over, and oh yeah the actual erorr was from an opcode I programmed myself i simply mixed up some values.

CodeSlinger
March 3rd, 2009, 07:43
Ripping my code is fine, just make sure you understand it. Once you understand it i'd recommend rewriting it using your own style as this will help you tackle your next emulator. Plus you'll feel like you've achieved something if it is all your own work.

Have fun.

Death the Kid
March 3rd, 2009, 09:04
LoL yeah i know you mean pretty much did all of opcodes myself, although i had to read through your explanation of the draw opocode a good few times LoL, although still not getting the graphics API side of things too well will need to read that up some more, but i did implement sound using SDL mixer and am currently implementing a GUI for it and all

gzaloprgm
March 5th, 2009, 04:13
Today I finished my second (S)CHIP8 emulator (first was in c, this is in c++). I have removed sounds (I hate the annoying beep) and added a feature I haven't seen in this thread, graphics interpolation. I am using hq2x and hq4x and the quality IS IMPRESSIVE! High quality anti alias ! Look by yourself:

http://img16.imageshack.us/img16/2904/sant.th.png (http://img16.imageshack.us/my.php?image=sant.png)http://img15.imageshack.us/img15/1895/sblinky.th.png (http://img15.imageshack.us/my.php?image=sblinky.png)http://img14.imageshack.us/img14/7602/srace.th.png (http://img14.imageshack.us/my.php?image=srace.png)http://img13.imageshack.us/img13/7193/sspacefig.th.png (http://img13.imageshack.us/my.php?image=sspacefig.png)

http://img18.imageshack.us/img18/1006/4ant.th.png (http://img18.imageshack.us/my.php?image=4ant.png)http://img17.imageshack.us/img17/8487/4blinky.th.png (http://img17.imageshack.us/my.php?image=4blinky.png)http://img16.imageshack.us/img16/9407/4dragon1.th.png (http://img16.imageshack.us/my.php?image=4dragon1.png)http://img15.imageshack.us/img15/3200/4joust23.th.png (http://img15.imageshack.us/my.php?image=4joust23.png)

Comparing to a bilinear filter this looks much better.

ASAP I will upload the code so others can learn (I can't attach it here, it gives error "Unable to move/copy file ")

What do you think about it?

Cheers,
Gzaloprgm

CodeSlinger
March 5th, 2009, 07:43
Good job mate, looks impressive. Im looking forward to viewing the source code. If you're having trouble getting a host for your code then I'll happily host it for you. Just PM me if you want.

hap
March 5th, 2009, 12:15
http://img16.imageshack.us/img16/9407/4dragon1.th.png (http://img16.imageshack.us/my.php?image=4dragon1.png)no pants :yucky:

gzaloprgm
March 5th, 2009, 20:20
Hahahaha, so funny. Hadn't realized that :P

Porting HQ2X / 4X to SDL apps is very easy!

As I stated, here is the code:

http://gontacalc.googlecode.com/files/chip8emu.zip

BTW, gontacalc is a proyect I am making which somehows relates to emulation.

Controls:
L : Shows the (pseudo) filebrowser to choose a rom
Right click in screen: Restart Rom
Wheel Up: Faster emulation
Wheel Down: Slower emulation
Numpad is mapped to CHIP8 Keyboard

Cheers,
Gzaloprgm

jaskt7
March 10th, 2009, 20:39
Hi, I am making a CHIP8 emu in Flash (don't make fun, just testing it) and I am having a few issues. I don't know if any of you know actionscript or not, but if you could just look through the code I would appreciate it.


http://babbage.cs.missouri.edu/~jaskt7/chip8/chip.html

the code for it is posted on the page, if you need to fla file,

http://babbage.cs.missouri.edu/~jaskt7/chip8/chip_cs3.fla


Anyways, the problem I am having is that the game 'brix' is not playing correctly. I haven't tried other games yet, but pong seems to work correctly.

The problem 'brix' is having is that the ball is hitting invisible (non-existent) bricks, including some that wouldn't even be there in the first place. Also, sometimes (a lot of times) the ball will go straight through many bricks without hitting them.

jaskt7
March 11th, 2009, 14:45
Never mind, I fixed the problem. There was an issue in my draw function, I guess the flag was not getting set right or something.

Thesuperchang
March 19th, 2009, 05:51
Just in case anyone was losing some sleep, I have an explanation on what BCD is, and its purpose.

Pretty much, its a verbose way to store decimal number in binary form. It is done as stated below;

ZXY is an integer where Z represents 100's, X represents 10's and Y represents 1's.
If we would want to convert ZXY to binary we would do the regular divide and remainder theorem or whatever you want.

If we want the BCD representation of ZXY, it is very simple. Given that
0 <= Z <= 9
0 <= X <= 9
0 <= Y <= 9
We write BCD as
zzzz xxxx yyyy
where z's, x's, y's are binary values for X, Z, Y. As an example;
convert 132 to BCD and Binary;
2's comp. binary = 1000 0100 = 0x84
BCD = 0001 0011 0010 = 0x132

Now you may be asking yourself, why the fuck! Well the idea is that processing is reduced at the expense of memory. A case may arise that numbers are inputted one digit at a time (Calculators for example). It is very simple to capture one digit at a time and the combine right before processing the number.

I hope that helps for some curious people. I know that when simulating the chip8, i8080 and Z80 I always cringed at the existence of BCD opcodes.

hernaldo2009
April 7th, 2009, 16:45
Hi guys:

I just finished my first emulator in C# 1.0, but works very slowly.
I build a FPS counter and only i can run to 6 FPS!

I Need help with the Timers, because i don't know how accelerate the code

This is my code (of course, is a bit messy):



using System;
using System.Drawing;
using SdlDotNet;
using SdlDotNet.Sprites;

using System.Runtime.InteropServices; //beep

using System.IO;


namespace Chip8_2
{
public class cap1
{

#region variables emulador

const int multiplicadorPixel = 5;
Surface srf = null;
int frames = 0;

const int START_ADDR = 0x200;
const int MEMORY_SIZE = 0xFFF;
const int MAX_KEYS = 16;
const int MAX_STACKSIZE = 16;
const int MAX_REGISTERS = 16;
const int RES_X = 64; //resolucion original 64x32
const int RES_Y = 32;
const int CHIP8_FNT_Y = 5;
const int SPRITE_X = 8;

int opCode1 = 0;
int opCode2 = 0; //X
int opCode3 = 0; //Y
int opCode4 = 0;


int[,] arrcScreen = new int[RES_X, RES_Y];

//Chip 8 tiene 2 timers: Delay Timer y Sound Timer
int cDelayTimer;
int cSoundTimer;


/// msec per timer tick, default is about 16
/// </summary>
public const int TIMER_MSEC_PER_TICK = 16;

//Used to control the timers in the Chip
DateTime _lastTime = DateTime.Now;

//high resolution delay timer implementation
private double _timerDelay;

// Delay timer, each value represents is about 16 msec
public byte TimerDelay
{
get { return (byte)_timerDelay; }
set { _timerDelay = value; }
}

// Sound timer - the speaker beeps if non-zero
// Each value is about 16 msec
public byte TimerSound
{
get { return (byte)_timerSound; }
set { _timerSound = value; }
}

//high resolution sound timer implementation
private double _timerSound;

DateTime fps_inicio = DateTime.Now;

[DllImport("Kernel32.dll")] //para beep
public static extern bool Beep(UInt32 frequency, UInt32 duration);


int[] arrcMemory = new int[MEMORY_SIZE];
int[] V = new int[MAX_REGISTERS];
int opCode;
int sPC;
int sI;
int sSP;
int[] sStack = new int[MAX_STACKSIZE];
int KK;

int[] arrcFontC8 = {
0xF0, 0x90, 0x90, 0x90, 0xF0, // Values For 0
0x60, 0xE0, 0x60, 0x60, 0xF0, // Values For 1
0x60, 0x90, 0x20, 0x40, 0xF0, // Values For 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // Values For 3
0x90, 0x90, 0xF0, 0x10, 0x10, // Values For 4
0xF0, 0x80, 0x60, 0x10, 0xE0, // Values For 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // Values For 6
0xF0, 0x10, 0x10, 0x10, 0x10, // Values For 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // Values For 8
0xF0, 0x90, 0xF0, 0x10, 0x10, // Values For 9
0x60, 0x90, 0xF0, 0x90, 0x90, // Values For A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // Values For B
0x70, 0x80, 0x80, 0x80, 0x70, // Values For C
0xE0, 0x90, 0x90, 0x90, 0xE0, // Values For D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // Values For E
0xF0, 0x90, 0xF0, 0x80, 0x80 // Values For F
};


private bool[] KEYS_PRESSED = {false,false,false,false,false,false,fal se,false,false,false,false,false,false,f alse,false,false};
const int TECLA_1 = 0;
const int TECLA_2 = 1;
const int TECLA_3 = 2;
const int TECLA_4 = 3;
const int TECLA_Q = 4;
const int TECLA_W = 5;
const int TECLA_E = 6;
const int TECLA_R = 7;
const int TECLA_A = 8;
const int TECLA_S = 9;
const int TECLA_D = 10;
const int TECLA_F = 11;
const int TECLA_Z = 12;
const int TECLA_X = 13;
const int TECLA_C = 14;
const int TECLA_V = 15;

// mapea el teclado qwerty al teclado del chip8
private byte[] KeyMap = {
0x01,0x02,0x03,0x0C,
0x04,0x05,0x06,0x0D,
0x07,0x08,0x09,0x0E,
0x0A,0x00,0x0B,0x0F };
// desmapea el teclado del chip8 al teclado qwerty
private byte[] KeyUnMap = {
0x0D,0x00,0x01,0x02,
0x04,0x05,0x06,0x08,
0x09,0x0A,0x0C,0x0E,
0x03,0x07,0x0B,0x0F };

#endregion



public cap1()
{
//resoluci n
Video.SetVideoModeWindow(RES_X*multiplic adorPixel, RES_Y*multiplicadorPixel);
// Limpar la pantalla (dejarla negra)
Video.Screen.Fill(Color.Black);
Video.WindowCaption = "Emulador Chip8 v0.01 - FPS: ?";

srf = new Surface(RES_X*multiplicadorPixel, RES_Y*multiplicadorPixel);
srf.Fill(Color.Black);

ResetHardware();
CargarROM("PONG"); //PONG PONG2

//_lastTime = DateTime.Now;
Events.Tick += new TickEventHandler(Events_Tick);
Events.Quit += new QuitEventHandler(Events_Salir);
}



private void Events_Salir(object sender, SdlDotNet.QuitEventArgs e)
{
Events.QuitApplication();
}

public void Run()
{

Events.KeyboardDown +=new KeyboardEventHandler(this.KeyboardPresse d);
Events.KeyboardUp +=new KeyboardEventHandler(this.KeyboardReleas ed);
Events.Run();
}

private void KeyboardPressed(object sender, KeyboardEventArgs e)
{
//Salimos del juego con tecla Escape
if(e.Key == Key.Escape)
{
Events.QuitApplication();
}

// activa la tecla presionada
if(e.Key == Key.One) {KEYS_PRESSED[TECLA_1] = true;}
if(e.Key == Key.Two) {KEYS_PRESSED[TECLA_2] = true;}
if(e.Key == Key.Three) {KEYS_PRESSED[TECLA_3] = true;}
if(e.Key == Key.Four) {KEYS_PRESSED[TECLA_4] = true;}
if(e.Key == Key.Q) {KEYS_PRESSED[TECLA_Q] = true;}
if(e.Key == Key.W) {KEYS_PRESSED[TECLA_W] = true;}
if(e.Key == Key.E) {KEYS_PRESSED[TECLA_E] = true;}
if(e.Key == Key.R) {KEYS_PRESSED[TECLA_R] = true;}
if(e.Key == Key.A) {KEYS_PRESSED[TECLA_A] = true;}
if(e.Key == Key.S) {KEYS_PRESSED[TECLA_S] = true;}
if(e.Key == Key.D) {KEYS_PRESSED[TECLA_D] = true;}
if(e.Key == Key.F) {KEYS_PRESSED[TECLA_F] = true;}
if(e.Key == Key.Z) {KEYS_PRESSED[TECLA_Z] = true;}
if(e.Key == Key.X) {KEYS_PRESSED[TECLA_X] = true;}
if(e.Key == Key.C) {KEYS_PRESSED[TECLA_C] = true;}
if(e.Key == Key.V) {KEYS_PRESSED[TECLA_V] = true;}
}

private void KeyboardReleased(object sender, KeyboardEventArgs e)
{
// activa la tecla presionada
if(e.Key == Key.One) {KEYS_PRESSED[TECLA_1] = false;}
if(e.Key == Key.Two) {KEYS_PRESSED[TECLA_2] = false;}
if(e.Key == Key.Three) {KEYS_PRESSED[TECLA_3] = false;}
if(e.Key == Key.Four) {KEYS_PRESSED[TECLA_4] = false;}
if(e.Key == Key.Q) {KEYS_PRESSED[TECLA_Q] = false;}
if(e.Key == Key.W) {KEYS_PRESSED[TECLA_W] = false;}
if(e.Key == Key.E) {KEYS_PRESSED[TECLA_E] = false;}
if(e.Key == Key.R) {KEYS_PRESSED[TECLA_R] = false;}
if(e.Key == Key.A) {KEYS_PRESSED[TECLA_A] = false;}
if(e.Key == Key.S) {KEYS_PRESSED[TECLA_S] = false;}
if(e.Key == Key.D) {KEYS_PRESSED[TECLA_D] = false;}
if(e.Key == Key.F) {KEYS_PRESSED[TECLA_F] = false;}
if(e.Key == Key.Z) {KEYS_PRESSED[TECLA_Z] = false;}
if(e.Key == Key.X) {KEYS_PRESSED[TECLA_X] = false;}
if(e.Key == Key.C) {KEYS_PRESSED[TECLA_C] = false;}
if(e.Key == Key.V) {KEYS_PRESSED[TECLA_V] = false;}
}

[STAThread]
static void Main()
{
cap1 juego = new cap1();

juego.Run();
}

#region emulador


private void Events_Tick(object sender, TickEventArgs e)
{
Emula();
}

public void Emula()
{


FetchOpcode();
ExecuteOpcode();

//beep
if (cSoundTimer > 0)
Beep(500, 1);

//DateTime now = DateTime.Now;
// calculate how much time passed since the last update
// double elapsedMsec = (now - _lastTime).TotalMilliseconds;

double tiempoPasado = (DateTime.Now - fps_inicio).TotalMilliseconds;

if (tiempoPasado > 1000) // cada un segundo grabo los Frames, asi calculo los frames por segundo (deber퀀an ser 60)
{
Video.WindowCaption = "Emulador Chip8 v0.01 - FPS: " + frames.ToString();
frames = 0;
fps_inicio = DateTime.Now;
}


if (cDelayTimer > 0) { cDelayTimer--; }
if (cSoundTimer > 0) { cSoundTimer--; }



}


void ResetHardware()
{
// Resetting Timers.
cDelayTimer = 0x0;
cSoundTimer = 0x0;

// Resetting Pointing Variables.
opCode = 0x0;
sPC = START_ADDR;
sSP = 0x0;
sI = 0x0;

// Clearing Memory.
for (int nAddr = 0; nAddr < MEMORY_SIZE; nAddr++)
{
arrcMemory[nAddr] = 0x0;
}

// Clearing Registers.
for (int nCurrReg = 0; nCurrReg < MAX_REGISTERS; nCurrReg++)
{
arrcMemory[nCurrReg] = 0x0;
}

// Clearing Stack.
for (int nCurrItem = 0; nCurrItem < MAX_STACKSIZE; nCurrItem++)
{
sStack[nCurrItem] = 0x0;
}

// Load Fontset To Memory.
LoadFontset(); //Las fuentes se usan para pintar palabras, por ej, los marcadores del juego PONG [0-0]
}


void LoadFontset()
{
for (int nIndex = 0; nIndex < 80; nIndex++)
{
arrcMemory[nIndex] = arrcFontC8[nIndex];
}
}


bool CargarROM(string ruta_rom)
{
FileStream rom = new FileStream(@ruta_rom,FileMode.Open);

if (rom.Length == 0)
{
Console.Write("Error: Archivo daကado o vac퀀o");
return false;
}

for (int i = 0; i<rom.Length; i++)
arrcMemory[START_ADDR + i] = (byte) rom.ReadByte(); //comenzamos de 200

rom.Close();

return true;


}

void FetchOpcode()
{
// Get Opcode From Memory (2 Bytes).
opCode = arrcMemory[sPC] << 8 | arrcMemory[sPC+1];

// Advance Program Counter To Next Opcode.
sPC += 2;
}





void ExecuteOpcode()
{
// Logging Opcode to Log File.
//logOpcode(sPC - 2,opCode);

KK = (opCode & 0x00FF);
//REG_Y = (opCode & 0x00F0) >> 4;
//REG_X = (opCode & 0x0F00) >> 8;

// separo el opCode en 4 bytes
opCode1 = (opCode & 0xF000) >> 12;
opCode2 = (opCode & 0x0F00) >> 8; //X
opCode3 = (opCode & 0x00F0) >> 4; //Y
opCode4 = (opCode & 0x000F) >> 0;


// Identifying Opcodes by Their Left-Most Figure.
switch (opCode1)
{
// 0xxx Opcodes.
case (0x0):
{
// Identifying 0xxx Opcodes.
switch (opCode & 0x0FFF)
{
// Opcode 00E0: Clear Screen.
case (0x00E0):
{
ClearScreen();
break;
}
// Opcode 00EE: Return From Subroutine.
case (0x00EE):
{
ReturnFromSub();
break;
}
}
break;
}
// 1xxx Opcodes.
case (0x1):
{
// Opcode 1NNN: Jump To Address NNN.
JumpToAddr();
break;
}
// 2xxx Opcodes.
case (0x2):
{
// Opcode 2NNN: Call Subroutine At Address NNN.
CallSub();
break;
}
// 3xxx Opcodes.
case (0x3):
{
// Opcode 3XKK: Skip Next Instruction If VX == KK
SkipIfEql();
break;
}
// 4xxx Opcodes.
case (0x4):
{
// Opcode 4XKK: Skip Next Instruction If VX != KK
SkipIfNotEql();
break;
}
// 5xxx Opcodes.
case (0x5):
{
// Opcode 5XY0: Skip Next Instruction If VX == VY
SkipIfRegEql();
break;
}
// 6xxx Opcodes.
case (0x6):
{
// Opcode 6XKK: Assign Number KK To Register X.
AssignNumToReg();
break;
}
// 7xxx Opcodes.
case (0x7):
{
// Opcode 7XKK: Add Number KK To Register X.
AddNumToReg();
break;
}
// 8xxx Opcodes.
case (0x8):
{
// Identifying 8xxx Opcodes.
switch (opCode4)
{
// Opcode 8XY0: Assign From Register To Register.
case (0x0):
{
AssignRegToReg();
break;
}
// Opcode 8XY1: Bitwise OR Between Registers.
case (0x1):
{
RegisterOR();
break;
}
// Opcode 8XY2: Bitwise AND Between Registers.
case (0x2):
{
RegisterAND();
break;
}
// Opcode 8XY3: Bitwise XOR Between Registers.
case (0x3):
{
RegisterXOR();
break;
}
// Opcode 8XY4: Add Register To Register.
case (0x4):
{
AddRegToReg();
break;
}
// Opcode 8XY5: Sub Register From Register.
case (0x5):
{
SubRegFromReg();
break;
}
// Opcode 8XY6: Shift Register Right Once.
case (0x6):
{
ShiftRegRight();
break;
}
// Opcode 8XY7: Sub Register From Register (Reverse Order).
case (0x7):
{
ReverseSubRegs();
break;
}
// Opcode 8XYE: Shift Register Left Once.
case (0xE):
{
ShiftRegLeft();
break;
}
}
break;
}
// 9xxx Opcodes.
case (0x9):
{
// Opcode 9XY0: Skip Next Instruction If VX != VY
SkipIfRegNotEql();
break;
}
// Axxx Opcodes.
case (0xA):
{
// Opcode ANNN: Set Index Register To Address NNN.
AssignIndexAddr();
break;
}
// Bxxx Opcodes.
case (0xB):
{
// Opcode BNNN: Jump To NNN + V0.
JumpWithOffset();
break;
}
// Cxxx Opcodes.
case (0xC):
{
// Opcode CXKK: Assign Bitwise AND Of Random Number & KK To Register X.
RandomANDnum();
break;
}
// Dxxx Opcodes.
case (0xD):
{
// Opcode DXYN: Draw Sprite To The Screen.
DrawSprite();
break;
}
// Exxx Opcodes.
case (0xE):
{
// Identifying Exxx Opcodes.
switch (KK)//opCode & 0x00FF
{
// Opcode 0xEX9E: Skip Next Instruction If Key In VX Is Pressed.
case (0x9E):
{
SkipIfKeyDown();
break;
}
// Opcode 0xEXA1: Skip Next Instruction If Key In VX Is NOT Pressed.
case (0xA1):
{
SkipIfKeyUp();
break;
}
}
break;
}
// Fxxx Opcodes.
case (0xF):
{
// Identifying Fxxx Opcodes.
switch (KK)//opCode & 0x00FF
{
// Opcode FX07: Assign Delay Timer To Register.
case (0x07):
{
AssignFromDelay();
break;
}
// Opcode FX0A: Wait For Keypress And Store In Register.
case (0x0A):
{
StoreKey();
break;
}
// Opcode FX15: Assign Register To Delay Timer.
case (0x15):
{
AssignToDelay();
break;
}
// Opcode FX18: Assign Register To Sound Timer.
case (0x18):
{
AssignToSound();
break;
}
// Opcode FX1E: Add Register To Index.
case (0x1E):
{
AddRegToIndex();
break;
}
// Opcode FX29: Index Points At CHIP8 Font Char In Register.
case (0x29):
{
IndexAtFontC8();
break;
}
// Opcode FX30: Index Points At SCHIP8 Font Char In Register.
case (0x30):
{
IndexAtFontSC8();
break;
}
// Opcode FX33: Store BCD Representation Of Register In Memory.
case (0x33):
{
StoreBCD();
break;
}
// Opcode FX55: Save Registers To Memory.
case (0x55):
{
SaveRegisters();
break;
}
// Opcode FX65: Load Registers From Memory.
case (0x65):
{
LoadRegisters();
break;
}
}
break;
}
}
}

// --------------------------------------------------------------------------------------

void ClearScreen()
{
Video.Screen.Fill(Color.Black);
}

void ReturnFromSub()
{
// Going To Last Stack Item.
sSP--;

// Pointing Next Instruction To The Saved Position In The Stack.
sPC = sStack[sSP];
}

void JumpToAddr()
{
// Jumping To Given Address.
sPC = (opCode & 0x0FFF);
}

void CallSub()
{
// Saving Current Program Counter.
sStack[sSP] = sPC;
sSP++;

// Jumping To Subroutine At Given Address.
sPC = (opCode & 0x0FFF);
}

void SkipIfEql()
{
// Check If The Register Value Equals The Given Value.
if (V[opCode2] == KK)
{
// Skip Next Instruction.
sPC += 2;
}
}

void SkipIfNotEql()
{
// Check If The Register Value Differs From The Given Value.
if (V[opCode2] != KK)
{
// Skip Next Instruction.
sPC += 2;
}
}

void SkipIfRegEql()
{
// Check If The X Register Value Equals The Y Register Value.
if (V[opCode2] == V[opCode3])
{
// Skip Next Instruction.
sPC += 2;
}
}

void AssignNumToReg()
{
// Assign Given Number To Register.
V[opCode2] = KK;
}



private char getLower(int number)
{
return (char)(number&0xFF);
}

void AddNumToReg()
{
// Add Given Number To Register.
V[opCode2] += KK;
}

// --------------------------------------------------------------------------------------

void AssignRegToReg()
{
// Assign Register Y To Register X.
V[opCode2] = V[opCode3];
}

void RegisterOR()
{
// Assign Mutual OR On Registers To X.
V[opCode2] |= V[opCode3];
}

void RegisterAND()
{

// Assign Mutual AND On Registers To X.
V[opCode2] &= V[opCode3];
}

void RegisterXOR()
{
// Assign Mutual XOR On Registers To X.
V[opCode2] ^= V[opCode3];
}

void AddRegToReg()
{
//8XY4 Realiza esto: VX = VX + VY, VF = carry

// Keep Carry Indication In Register F.
V[0xF] = (V[opCode2] + V[opCode3]) >> 8;

// Add Register Y To Register X.
V[opCode2] += V[opCode3];
}

void SubRegFromReg()
{
// Saving Negated Borrow Indication In Register F.
if (V[opCode2] >= V[opCode3])
{
V[0xF] = 0x1;
}
else
{
V[0xF] = 0x0;
}

// Subtracting Register Y From Register X.
V[opCode2] -= V[opCode3];
}

void ShiftRegRight()
{
V[0xF] = V[opCode2] & 0x1;
V[opCode2] >>= 1;
}

void ReverseSubRegs()
{
if (V[opCode2] <= V[opCode3])
{
V[0xF] = 0x1;
}
else
{
V[0xF] = 0x0;
}

}

void ShiftRegLeft()
{
V[0xF] = V[opCode2] & 0x10;
V[opCode2] <<= 1;
}

// --------------------------------------------------------------------------------------

void SkipIfRegNotEql()
{
// Check If The X Register Value Differs From The Y Register Value.
if (V[opCode2] != V[opCode3])
{
// Skip Next Instruction.
sPC += 2;
}
}

void AssignIndexAddr()
{
// Set Index Register To Point To Address NNN.
sI = opCode & 0x0FFF;
}

void JumpWithOffset()
{
// Jump To Address NNN + Offset From Register 0.
sPC = (opCode & 0x0FFF) + V[0x0];
}

void RandomANDnum()
{
// Generating Random Number With Given Number.

Random r = new Random((int)DateTime.Now.Ticks);
int rnd = r.Next();
if (rnd < 0) rnd = rnd*-1; //siempre positivo
V[opCode2] = rnd & KK;

}

/*screen is 64x62 pixels
Todos los drawings son hechos en modos XOR.
Cuando uno o mas pixels son borrados mientras un sprite es pintado,
el registro VF se setea en 01, sino queda en 00.
*/

void DrawSprite()
{
// Resetting Collision Detection.
V[0xF] = 0x0;

if ((opCode & 0x000F) == 0) //opCode & 0x000F =opcode4
{
// Draw SCHIP8 Sprite Of Size 16x16.
// * Not Implanted Yet :P *
}
else
{

// Draw CHIP8 Sprite Of Size 8xN

for (int nSpriteY = 0; nSpriteY < opCode4; nSpriteY++)
{
for (int nSpriteX = 0; nSpriteX < SPRITE_X; nSpriteX++)
{

int x = (arrcMemory[sI + nSpriteY] & (0x80 >> nSpriteX));


if ( x != 0)
{
// Checking For Collision (Overwrite Pixel).
int xx = (V[opCode2] + nSpriteX);
int yy = (V[opCode3] + nSpriteY);

//grabaLog ("xx=" + xx.ToString() + " xx%64=" + (xx%64).ToString() + " yy=" + yy.ToString() + " yy%32=" + (yy%32).ToString());
if (arrcScreen[xx % 64, yy % 32] == 1) //% resto o modulo de una division, ejemplo 32%64=32, 0%32=0, 1%32=1, 31%32=31, 32%32=0, 33%32=1 (aqui se caia ya que llegaba a 32 y el max que puede alcanzar es 31)
V[0xF] = 1;

arrcScreen[xx % 64, yy % 32] ^= 1; //XOR 0^1=1, 1^0=1, 1^1 =0, 0^0=0



}
}
}
}

UpdateScreen();
}

void SkipIfKeyDown()
{
if(KEYS_PRESSED[KeyUnMap[V[opCode2]]] == true)
sPC += 2;
}

void SkipIfKeyUp()
{
if(KEYS_PRESSED[KeyUnMap[V[opCode2]]] == false)
sPC += 2;
}

void AssignFromDelay()
{
V[opCode2] = cDelayTimer;
}

void StoreKey()
{
for (int nKey = 0; nKey < KEYS_PRESSED.Length; nKey++)
{
if (KEYS_PRESSED[nKey] == true)
{
V[opCode2] = nKey;
}
}
}

void AssignToDelay()
{
cDelayTimer = V[opCode2];
}

void AssignToSound()
{
cSoundTimer = V[opCode2];
}

void AddRegToIndex()
{
sI += V[opCode2];
}

void IndexAtFontC8()
{
sI = (V[opCode2] * 0x5);
}

void IndexAtFontSC8()
{
// Not Implemanted yer, SCHIP8 Function.
}

void StoreBCD()
{
int nRegister = (int) V[opCode2];
arrcMemory[sI] = nRegister / 100;
arrcMemory[sI + 1] = (nRegister / 10) % 10;
arrcMemory[sI + 2] = nRegister % 10;
}

void SaveRegisters()
{
for (int nIndex = 0; nIndex <= opCode2; nIndex++)
{
arrcMemory[sI++] = V[nIndex];
}
sI += 1;
}

void LoadRegisters()
{
for (int nIndex = 0; nIndex <= opCode2; nIndex++)
{
V[nIndex] = arrcMemory[sI++];
}
sI += 1;
}



void UpdateScreen()
{
int nDrawX;
int nDrawY;

for (nDrawX = 0; nDrawX < RES_X; nDrawX++)
{
for (nDrawY = 0; nDrawY < RES_Y; nDrawY++)
{
if (arrcScreen[nDrawX, nDrawY] != 0)
{
Box b = new Box(new Point(nDrawX, nDrawY), new Size(1,1));
srf.DrawFilledBox(b, Color.White);
}
else
{
Box b = new Box(new Point(nDrawX, nDrawY), new Size(1,1));
srf.DrawFilledBox(b, Color.Black);
}
}
}

Surface srf_back = new Surface(srf);
srf.Scale(multiplicadorPixel);
Video.Screen.Blit(srf);
srf = srf_back; //para no ir Scalando indefinidamente
Video.Screen.Update();

frames ++; //para el calculo de FPS


}

#endregion



public void grabaLog(string data)
{
Console.WriteLine(data);
}

}




}

Doomulation
April 7th, 2009, 17:45
I would say that's because languages such as C# is no good for emulators. They are too slow.
But... have you used a profiler?

hernaldo2009
April 7th, 2009, 18:23
Sorry for the question. but what is a Profiler?

other questions:

-¿if i program the same code but in C or C++ i can get best times that in C#?

- if i like develop a Chip 8 emulator, what is more fast: C or C++? I ask for learning the languaje.

- if I use Visual Studio .Net 2003, you recommend to me using VC++ or not?


thanks.

swatgod
April 8th, 2009, 16:31
if you do not know either language than i suggest you forget about making an emulator for right now, making an emulator is hardly the type of learning program you want to be making because of the learning curve required by making one.

if you know neither c or c++, than i suggest if you want an easier time learning a programming language than go for c#, i know some old school coders may flame me for saying that but its a much easier language to learn on and i have proved in terms of drawing, if you play your cards right, can be much faster and if you need such an example i do have one, it can render a frame on xp in 0 ms and the same technique in c/c++ was a lot slower.

edit: i just saw someone used sdldotnet in c#, i HIGHLY suggest you get rid of it and use managed directx, sdldotnet i used for my chip 8 and it was very slow and its horribly made(check memory use when its running). i tried it on my space invaders and its memory use sky rocketed to 200 mb from its buggy nature, so i highly suggest anyone coding an emu in c#, to use managed directx as it made a world of difference in speed and ease of use.

method i used was to create 2 triangles the size of the screen i needed than create a texture and locked the texture and wrote my pixels to it than unlocked and allowed directx to render the texture on top of the triangles. i only used that method on space invaders but i am certain its just as fast as space invaders uses a much higher res than chip 8 does. the setup of managed directx is about just under 200 lines for the entire thing including code to allow it to reinitialize if device was lost.

hernaldo2009
April 8th, 2009, 22:03
thanks.
ok, then I will do the same version of the emulator, but with Managed Direct X + C#. What version of Direct X can I use?

swatgod
April 9th, 2009, 01:46
just use the latest version, most of the references only have 1 copy.

hernaldo2009
April 9th, 2009, 15:38
Yesterday I make the same version of emulator but in console mode, printing ascii characters, and i get 18 FPS, 3 times most fast than sdldotnet. I will see how many FPS can get now.

Hellfish
April 24th, 2009, 08:57
Hi!

New guy here, also working on a CHIP8 interpreter. :)
Like hernaldo2009, I'm using C# with SDLDotNet, though I'm getting better performance (right around 30FPS for most CHIP8 games). However, now I got into adding SCHIP support and was wondering since I couldn't find specific info anywhere (I've checked Zophar's, David Winter's, the archived goldroad,codeslinger.co.uk and Doomulation's document).

The DXYN opcode: If in CHIP8 mode, the resulting sprite will be 8xN.And in SCHIP mode, if N is 0, the sprite will be 16x16. But in SCHIP mode, if N isn't 0, will the sprite be 8xN or 16xN? (Also, in CHIP8 mode, if N is 0, what will draw?Nothing?)

Thanks for a quality thread, so far! :)

EDIT: Also, I've been working on some test ROMs for various opcodes that can be difficult to verify as working, BCD (FX33) one is done so far but I don't know wether I should post it, considering the rules.

EDIT 2: I figured I'll attach the emulator. It requires .NET 2.0 and SdlDotNet Runtime (cs-sdl.sourceforge.net). I don't think it works with Mono. It allows for key remapping inside the program. Target FPS is what FPS SDLDotNet will attempt to render at.After a little modification, I get between 25-60 fps depending on which ROM.

Source (http://cmmdb.heliohost.org/serve/Sharp8-src.rar) (Does not include the source of the virtual listbox component.)
Binary (http://cmmdb.heliohost.org/serve/Sharp8-bin.rar)

serge2k
June 1st, 2009, 09:08
i've finally got mine to the point where it is capable of loading and running all the games with proper graphics, sound (sorta, it beeps anyway), and collision detection.

So pong(2), breakout, brix, and a couple of others are all playable.

I still need to add timing and figure out some optimizations (space invaders is very clumsy and slow, basically it's incredibly difficult/impossible to pass the first level)

Still, I'm pretty happy. I've attempted this before but always got stuck at the graphics stage. Getting some good C++ practice for my OS course next year (everything up to this point is java, but the prof let us make the choice).

I was wondering if there is an easy way to cut down on the flickering? I really don't know much directX at this ponit.

Doomulation
June 1st, 2009, 15:30
I was wondering if there is an easy way to cut down on the flickering? I really don't know much directX at this ponit.

No reliable way. It's a flaw of the hardware itself - it simply lacked instructions for collision, so the games tend to draw on already drawn parts every frame or so, and then checks the collision bits.

There might be a way to "hack" this system by, for example, guessing if the game did it to know if there was a collision or not, and if it was merely for checking collision, then skipping the erasing part and the subsequent draw to redraw the part that was erased.
But sometimes games just wants to erase pixels on the screen, which can also be done via the draw opcode.

So no, no reliable way.

AceHack
June 22nd, 2009, 08:39
No reliable way. It's a flaw of the hardware itself - it simply lacked instructions for collision, so the games tend to draw on already drawn parts every frame or so, and then checks the collision bits.

There might be a way to "hack" this system by, for example, guessing if the game did it to know if there was a collision or not, and if it was merely for checking collision, then skipping the erasing part and the subsequent draw to redraw the part that was erased.
But sometimes games just wants to erase pixels on the screen, which can also be done via the draw opcode.

So no, no reliable way.


Hey Doom I'm writing a silverlight Chip-8 Emu to learn to write emulators because I want to write a SNES or NES silverlight emu. My Chip-8 is working great on many games but I noticed on PONG some flickering. How did you fix that on yours. Thanks so much for your help. Also do you have VS2008 and Silverlight 3 Beta. I'd love to let you look over my source but it's in Silverlight 3 Beta. Thanks again.

Also how do you set your timing for the CPU cycle, it works so great on every game. On mine some games seem slow and some seem fast. Thanks.

Also do you have any idea why blinky is not working? It drawing some really weird stuff.

Also in your source it looks like you update your delay register only after every 20 op codes is this correct? or what am I missing when I read your source. Thanks.

Doomulation
June 22nd, 2009, 12:54
Hey Doom I'm writing a silverlight Chip-8 Emu to learn to write emulators because I want to write a SNES or NES silverlight emu. My Chip-8 is working great on many games but I noticed on PONG some flickering. How did you fix that on yours.
I used a game-specific hack.


Thanks so much for your help. Also do you have VS2008 and Silverlight 3 Beta. I'd love to let you look over my source but it's in Silverlight 3 Beta. Thanks again.
Sorry no. I don't program Silverlight. I only do C/C++ (preferably C++).


Also how do you set your timing for the CPU cycle, it works so great on every game. On mine some games seem slow and some seem fast. Thanks.
I can't remember 100%, but I believe I fixed the number of opcodes per second or some such. I would have to re-read and re-test the code to understand it now :P


Also do you have any idea why blinky is not working? It drawing some really weird stuff.
No idea. It could be a thousand things wrong.
You may want to invest in a small debugger.
That was how I found and fixed most problems.


Also in your source it looks like you update your delay register only after every 20 op codes is this correct? or what am I missing when I read your source. Thanks.
It has to do with the timing, but again, I'm not sure how I did it, exactly.

elizeucoder
August 3rd, 2009, 23:50
Hi guys this is my first emulator i've made.
it's written in java
It's working well with the game tic-tac-toe, but games like space invaders outside always the screen coordinates getting always an error of array bound excetion.
I need some help of you guys,mainly in the the DRW_Vx_Vy_nibble() method in the source code below the links of the source and emulator


Sorry for my english.


SOURCE: LINK (http://www.megaupload.com/?d=L5USAPGM)
EMULATOR: LINK (http://www.megaupload.com/?d=7F34K5AK)

this is the method that sometimes have problems.


public void DRW_Vx_Vy_nibble()
{
int x=(op & 0xf00)>>8;
int y=(op & 0xf0)>>4;

int _x=V[x],_y=V[y];

int n=(op & 0xf);
String _I=Integer.toHexString(I);
String tmp=" x=V"+x+" y=V"+y;
Debug("Display "+n+"-byte sprite starting at memory location 0x"+_I+" at ("+V[x]+", "+V[y]+"), "+tmp+" set VF = collision.");
PC+=2;
/*
System.out.print("data to be written: ");
for(int k=0;k<x;k++)
{
System.out.print(MEMORY[I]+" ");
}
System.out.println();


for(int _y=0;_y<n;_y++)
{

DrawPixelRow(V[x],V[y]+_y,MEMORY[I+_y],x,y);
//System.out.println();
}
*/


int pixel=0;
for(int i=0;i<n;i++)
{
int data=MEMORY[I+i];

for(int j=0;j<8;j++)
{

if((data & (0x80 >> j))!=0)
{
if(pixel==1)
V[0xf]=1;

pixel=video[_x+j][_y+i];



video[_x+j][_y+i]^=1;


} //End child for


}//End parent for

}//End draw method

//video[100][100]=1;
this.VideoChanged=true;

}

Facon
September 27th, 2009, 16:30
I have one problem.

I have in memory:

0x00E3

The instruction is 00E0 - CLS.

I should interpret it as instruction or I ignore it?

PD: Sorry for my bad English.

elizeucoder
September 27th, 2009, 18:20
You should not ignore it.

Facon
October 3rd, 2009, 10:10
I have now another problem with Counter, I don't know the clock's cycles of Chip8 neither cycles per each instruction.

What I should do?

PD: Sorry for my bad English.

elizeucoder
October 3rd, 2009, 16:13
The documentation of chip-8 don't talk about the cycles, because chip-8 hasn't interrupts.
so your cpu cicle code must be something like:



while(1)
{

fetchopcode(),

interpretopcode();

}



Hope i helped.

Add me on msn to talk better: mixeliseu13_8 <at> hotmail.com

Facon
October 4th, 2009, 17:53
Ok, I add you on MSN ;) .

Tronix
October 14th, 2009, 12:14
Hi guys, i development my own chip8 emu in Delphi programming languarge, but i dont' very well C. I get some help from BadMagick Chip8 C sources (thanks to author). And i have some problems with my draw algorithm: when part of sprite need to be drawn within screen, other part don't show (see pic). Can any-body help me, plz?

My draw procedure (at temporary Display[64*32] buffer):


Procedure TNewThread.Draw;
Var
x,y : Byte;
loop_x,loop_y : Byte;
x_pos,y_pos : Byte;
pixel : Byte;
Begin
// Get X and Y coordinates
x := ReadRegister((op_code and $0F00) shr 8);
y := ReadRegister((op_code and $00F0) shr 4);

// Reset VF register
WriteRegister($0F, $00);

for loop_y := 0 to (op_code and $000F)-1 do
Begin
// Assign a variable to current line
pixel := ReadMemory(rI + loop_y);
for loop_x := 0 to 7 do
Begin
// Check each bit to see if it is on or off
if pixel and ($80 shr loop_x) <> 0 then
Begin
x_pos := (x + loop_x);
y_pos := (y + loop_y);
if Display[x_pos + y_pos* 64] = $01 then WriteRegister($0F, $01);
Display[x_pos + y_pos * 64] := Display[x_pos + y_pos * 64] xor 1;
End;
End;
End;

Render(x,y,(op_code and $000F)-1);
End;


So, next procedure draw Sprite at the canvas :


Procedure TNewThread.Render(ppx,ppy,lp:Byte);
Var
py,px : word;
x,y : Byte;
Begin
Form1.Image1.Canvas.Pen.Width := 2;
// Form1.Image1.Canvas.Pen.Color := clBlack;
py := ppy*8;
For y := ppy to ppy+lp do
Begin
px := ppx*8;
For x := ppx to ppx+7 do
Begin
If Display[x+y*64] <> 0 then
Begin
Form1.Image1.Canvas.Pen.Color := clGreen;
Form1.Image1.Canvas.Brush.Color := clGreen;
end
else
begin
Form1.Image1.Canvas.Pen.Color := clBlack;
Form1.Image1.Canvas.Brush.Color := clBlack;
end;
Form1.Image1.Canvas.Rectangle(px,py,px+7 ,py+7);
inc(px,8);
End;
inc(py,8);
End;
// writeln(ff,'end render');
End;


http://tronix286.pochta.ru/dxdx.png
PS: Sorry for my terrible English plz.

Tronix
October 15th, 2009, 14:29
Oh, okay. I fixed bug just using MOD operation. Thank you for your attention )

Facon
November 25th, 2009, 21:16
I'm finally finish my emulator in a dirty C (global variables, etc..., but readable) using SDL for screen and computer's beep for sound (review my code).

License: MIT License / Public Domain

Copy & reuse my code as you like, use as a reference ;) .

Plataform: GNU\Linux (It's code is portable try to compile in Windows and Mac OS X)

Download: http://www.mediafire.com/file/0ro1n4xuyzy/AC8E.zip

Instructions:

1º open application via console:
./chip8
2º click console and put in CASE the name of the game, for i.e.: MAZE
3º click the window
4º buttons:

1 2 3 4
q w e r
a s d f
z x c v

Problems:

1º A very slow emulator
2º Emulator don't detect very well pulsations, you need to repeat pulsation

A Greeting.

anikom15
December 23rd, 2009, 17:15
Ok, I'm making a CHIP-8 emulator for linux. I can't seem to get the emulator to read the ROM correctly, it justs loops over and over again and doesn't do anything. I'm suspecting this has to do with the PC variable. This is strict C99, no C++ code please. Also, if you could help me with the TODOs that would be great.


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "SDL.h"
#include "function.h"

/* If we ever needed any constants we'd put 'em here, but we don't need any
* constants */

main(int argc, char **argv)
{
/* generic math variables */
int i;
int n;
int len;

/* SDL specific crap variables */
SDL_Event event;
SDL_Surface* screen = NULL;
SDL_Surface* finalscreen = NULL;
unsigned int realx = 640;
unsigned int realy = 320;
int multiby = realx / 64;

/* SDL crap */
printf("Initializing SDL.\n");

if((SDL_Init(SDL_INIT_EVERYTHING) == -1)) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);

/* Video crap */
screen = SDL_SetVideoMode(realx, realy, 24, SDL_SWSURFACE);
if (screen == NULL) {
fprintf(stderr, "Couldn't set specified video mode: %s\n",
SDL_GetError());
exit(1);
}

printf("Initializing CHIP-8.\n");

/* All the crap we need for CHIP-8 */
unsigned int memory[0xfff];
unsigned int stack[0xf];
unsigned int V[0xf];
unsigned int I; // I ain't telling you what this is for.
unsigned int DT = 0;
unsigned int ST = 0;
unsigned int PC = 0x0;
unsigned int SP = 0x0;
unsigned int opcode;

unsigned int key[0xf];
/* set all the crappy keys to zero */
for (i = 0x0; i <= 0xf; i++)
key[i] = 0x0;

unsigned int xres = 64;
unsigned int yres = 32;

/* Put other crap variables here... */
Uint32 color0 = SDL_MapRGB(screen->format, 0, 0, 0);
Uint32 color1 = SDL_MapRGB(screen->format, 170, 170, 170);
Uint32 cpixel;
int wrapx = 0, wrapy = 0;

printf("Opening ROM...\n");

/* File loading crap */
FILE *f = fopen(argv[1], "rb");
fread(&memory, 1, 4096, f);
fclose(f);
printf("ROM loaded.\nRunning happily.\n");

/* The crappy main loop */
for(;;) {
/* TODO: program should exit when ESC is hit */
opcode = memory[PC++];

/* WARNING: massive switch statement */
switch (opcode) {
case 0x0:
if ((opcode& 0x00ff) == 0xe0) {
/* I've gone blind!!! */
SDL_FillRect(SDL_GetVideoSurface(), NULL, color0);
break;
}
if ((opcode& 0x00ff) == 0xee) {
/* What is this I don't even... */
PC = stack + SP;
SP--;
break;
}
break;
case 0x1:
/* Dangnabit */
PC = opcode& 0x0fff;
break;
case 0x2:
/* I hope she made lots of spaghetti code! */
SP++;
stack[SP] = PC;
PC = opcode& 0x0ff;
break;
case 0x3:
/*-*/ // Cassette tape?
if (V[opcode& 0x0f00] == (opcode& 0x00ff))
PC += 2;
break;
case 0x4:
/* GYIYG lives. */
if (V[opcode& 0x0f00] != (opcode& 0x00ff))
PC += 2;
break;
case 0x5:
/* *fart* */
if (V[opcode& 0x0f00] == (opcode& 0x00f0))
PC += 2;
break;
case 0x6:
/* Do I have to put a comment on every one of these? */
V[opcode& 0x0f00] = (opcode& 0x00ff);
break;
case 0x7:
/* VX = VX + KK */
V[opcode& 0x0f00] += (opcode& 0x00ff);
break;
case 0x8:
/* Low level programming is fun for the first 10 minutes. */
/* On a serious note, this section deals with bitwise
* operations */
if ((opcode& 0x000f) == 0) {
V[opcode& 0x0f00] = (opcode& 0x00f0);
break;
}
if ((opcode& 0x000f) == 0x1) {
V[opcode& 0x0f00] |= V[opcode& 0x00f0];
break;
}
if ((opcode& 0x000f) == 0x2) {
V[opcode& 0x0f00] &= V[opcode& 0x00f0];
break;
}
if ((opcode& 0x000f) == 0x3) {
V[opcode& 0x0f00] ^= V[opcode& 0x00f0];
break;
}
if ((opcode& 0x000f) == 0x4) {
V[opcode& 0x0f00] += V[opcode& 0x00f0];
if (V[opcode& 0x0f00] > 255)
V[0xf] = 1;
break;
}
if ((opcode& 0x000f) == 0x5) {
V[opcode& 0x0f00] -= V[opcode& 0x00f0];
if (V[opcode& 0x0f00] >= V[opcode& 0x00f0])
V[0xf] = 1;
else
V[0xf] = 0;
}
if ((opcode& 0x000f) == 0x6) {
if ((V[opcode& 0x0f00]& 0x0) == 1)
V[0xf] = 1;
V[opcode& 0x0f00] /= 2;
break;
}
if ((opcode& 0x000f) == 0x7) {
V[opcode& 0x00f0] -= V[opcode& 0x0f00];
if (V[opcode& 0x00f0] >= V[opcode& 0x0f00])
V[0xf] = 1;
else
V[0xf] = 0;
break;
}
if ((opcode& 0x000f) == 0xe) {
V[opcode& 0x0f00] *= 2;
if ((V[opcode& 0x0f00]& 0xf) == 1)
V[0xf] = 1;
break;
}
break;
case 0x9:
if (V[opcode& 0x0f00] != V[opcode& 0x00f0]) {
PC += 2;
break;
}
break;
case 0xa:
I = (opcode& 0x0fff);
break;
case 0xb:
PC = (opcode& 0x0fff) + V[0];
break;
case 0xc:
/* Oh no random! */
srand(time(NULL));
V[opcode& 0x0f00] = rand() & (opcode& 0x00ff);
break;
case 0xd:
/* Some sprite drawing! */
for (n = (opcode& 0x000f); n > 0; n--) {
while(V[opcode& 0x0f00] + len - wrapx > xres)
wrapx += xres;
while(V[opcode& 0x00f0] + n - wrapy > yres)
wrapy += yres;
len = 0;
PC = memory[I];
while (len <= 8) {
if (SDL_MUSTLOCK(screen)) {
if (SDL_LockSurface(screen) < 0) {
fprintf(stderr, "Can't lock screen: %s\n",
SDL_GetError());
exit(1);
}
}
cpixel = getpixel(screen, V[opcode& 0x0f00] + len,
V[opcode& 0x00f0] + n);
if (cpixel == color1)
V[0xf] = 1;
if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
if (SDL_MUSTLOCK(screen)) {
if (SDL_LockSurface(screen) < 0) {
fprintf(stderr, "Can't lock screen: %s\n",
SDL_GetError());
exit(1);
}
}
putpixel(screen, V[opcode& 0x0f00] + len - wrapx,
V[opcode& 0x00f0] + n - wrapy, color1);
if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
len++;
}
}
break;
case 0xe:
if ((opcode& 0x00ff) == 0x9e) {
if (key[opcode& 0x0f00] != 0)
PC += 2;
break;
}
if ((opcode& 0x00ff) == 0xa1) {
if (key[opcode& 0x0f00] == 0)
PC += 2;
break;
}
break;
case 0xf:
if ((opcode& 0x00ff) == 0x07) {
V[opcode& 0x0f00] = DT;
break;
}
if ((opcode& 0x00ff) == 0x0a) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_1:
key[0x1] = 1;
V[opcode& 0x0f00] = 0x1;
break;
case SDLK_2:
key[0x2] = 1;
V[opcode& 0x0f00] = 0x2;
break;
case SDLK_3:
key[0x3] = 1;
V[opcode& 0x0f00] = 0x3;
break;
case SDLK_4:
key[0xc] = 1;
V[opcode& 0x0f00] = 0xc;
break;
case SDLK_QUOTE:
key[0x4] = 1;
V[opcode& 0x0f00] = 0x4;
break;
case SDLK_COMMA:
key[0x5] = 1;
V[opcode& 0x0f00] = 0x5;
break;
case SDLK_PERIOD:
key[0x6] = 1;
V[opcode& 0x0f00] = 0x6;
break;
case SDLK_p:
key[0xd] = 1;
V[opcode& 0x0f00] = 0xd;
break;
case SDLK_a:
key[0x7] = 1;
V[opcode& 0x0f00] = 0x7;
break;
case SDLK_o:
key[0x8] = 1;
V[opcode& 0x0f00] = 0x8;
break;
case SDLK_e:
key[0x9] = 1;
V[opcode& 0x0f00] = 0x9;
break;
case SDLK_u:
key[0xe] = 1;
V[opcode& 0x0f00] = 0xe;
break;
case SDLK_SEMICOLON:
key[0xa] = 1;
V[opcode& 0x0f00] = 0xa;
break;
case SDLK_q:
key[0x0] = 1;
V[opcode& 0x0f00] = 0x0;
break;
case SDLK_j:
key[0xb] = 1;
V[opcode& 0x0f00] = 0xb;
break;
case SDLK_k:
key[0xf] = 1;
V[opcode& 0x0f00] = 0xf;
break;
default:
break;
}
break;
}
for (i = 0x0; i <= 0xf; i++)
key[i] = 0x0;
break;
}
break;
}
if ((opcode& 0x00ff) == 0x15) {
DT = V[opcode& 0x0f00];
break;
}
if ((opcode& 0x00ff) == 0x18) {
ST = V[opcode& 0x0f00];
break;
}
if ((opcode& 0x00ff) == 0x1e) {
I += V[opcode& 0x0f00];
break;
}
if ((opcode& 0x00ff) == 0x29) {
/* TODO: Hexadecimal font */
break;
}
if ((opcode& 0x00ff) == 0x33) {
/* TODO: BCD */
break;
}
if ((opcode& 0x00ff) == 0x55) {
for (i = 0x0; i <= (opcode& 0x0f00); i++) {
memory[I] = V[i];
I++;
}
break;
}
if ((opcode& 0x00ff) == 0x65) {
for (i = 0x0; i <= (opcode& 0xf00); i++) {
V[i] = memory[I];
I++;
}
break;
}
break;
default:
break;
}
SDL_UpdateRect(finalscreen, 0, 0, 0, 0);
PC += 2;
}
/* printf("THE WHOLE UNIVERSE IS GOING TO DIE!!!\n"); */
printf("Shutting down...\n");

SDL_Quit();

printf("Bye.\n");
exit(0);
}

MicBeaudoin
December 23rd, 2009, 19:37
It looks like you increment PC a bit too much.

You are incrementing it here:

<code>opcode = memory[PC++];</code>

and into several opcodes:

<code>PC += 2;</code>

thus increasing it by 3 bytes. Don't forget that all opcodes are 2 bytes long.
It looks like you are missing some " PC += 2" at several places too.

anikom15
December 23rd, 2009, 20:09
Where exactly am I missing PC += 2?

MicBeaudoin
December 24th, 2009, 02:55
Just one example:



case 0xa:
I = (opcode& 0x0fff);
// You should put PC += 2 here;
break;

Doomulation
December 24th, 2009, 20:31
A better solution might be to increment the PC at one place instead of several places. So after processing an opcode, add 2 to the PC. Not after each opcode.

Cyberman
December 24th, 2009, 20:37
A better solution might be to increment the PC at one place instead of several places. So after processing an opcode, add 2 to the PC. Not after each opcode.
What Doom (We are all DOOMED I TELL YA! err...) is trying to tell you it is good debugging/programming practice to be sure you do something in a place where it is most consistent. In this case after you have read a complete opcode (IE the opcode is now in your pile of data) you don't need a pointer to the current instruction. Therefore you can advance the PC there.

It is likely the original Chip 8 does this (as it technically uses an interpreter that reads the 'program').

Cyb

SH-2009
December 25th, 2009, 21:02
hello
I started this days to write a chip8 emulator i read documents about it and I understant how it work
i'm not good in C++ and I want to load file in the memory but i didn't know how please i need your help

anikom15
December 26th, 2009, 23:42
I already up the PC by 2 after the switch statement. Other times are for skipping instructions.

anikom15
December 27th, 2009, 20:49
Ok, if I try to run Pong it executes these opcodes:

c6b026a
b6daeaa2
366d422
7f015f0
87717c7
eaa271d6
a1e00160
27ba1e0
c60b6da
a1e00d60
d6dc028d
94878486
12871f61
82123f46
1690047
1630268
fe688a12
13fd580
13f1580
c812013f
2060c212
d422348e
fe680366
ff791612
179c812
18f00460
6c12fe76
29f165f2
157455d4
8080ee00
80
and then it exits.

perhaps it has to do with the file loading?

dstruktiv
January 21st, 2010, 01:36
Sorry for the thread dig but it's for a good cause :)

I'm new to emulation and tried to start with the GB but got confused so I decided to go with Chip 8.

As far as I can tell I have everything working in terms of cpu and memory.

I load pong to 0x200 and start processing and watching my debug output I can see it processing all the opcodes and just going for ever, every time it polls for input I just give it a random up or down key (Which would move the paddle) and well yeah there's no end to the pong game until it's terminated so that's exactly what I'd expect my emu to do :)

Now I'm at the point where I want to write the display code, it MUST be done using DX9 as my reason for learning emulation is to port stuff to the Xbox 360.

My emu actually runs on the 360 at the moment, just no screen output :p

At the moment my screen info is stored in a char array, ready to be output somehow...

I have got as far as creating a D3D device and implementing a simple update and render loop that clears out the old scene and displays the new one - At the moment that's simply a blue background.

I've been told I need to create a texture, create a an object to apply that texture to (A rect), then each update lock my texture, clear it out, plot the char array to it some how, unlock it, then render it.

I've been on Google for the last few hours and really don't seem to be getting anywhere. For things as simple as this the Xbox DX9 is identical to the PC DX9.

I have found a few open source Chip 8 emus that use SDL, so I can see how it's done using SDL which appears to be similar to what I'm trying to do using DX9 but obviously there is some differences.

Anyone that has any experience with emulation and DX9 I would really appreciate some help.

Doomulation
January 21st, 2010, 13:37
You should just need to draw triangles to the screen. Nothing more complicated.
Any DX9 tutorial should should help you with that, I think.
I know I wrote my emulator using DX9. And I did it using some basic triangles.

dstruktiv
January 22nd, 2010, 23:17
In case anyone ever need to do this with D3D9, create a quad, create a texture, each update call lock your texture, manually modify the pixel info as per below, unlock it, then apply it to your quad object and render it.

I still have some things to work out with some items coming and going with each alternating update call (As you can see below the ball is either coming or going) but below we have the first ever Chip 8 emulator running on the Xbox 360 :)


m_pd3dDevice ->CreateTexture(64, 32, 1, 0, D3DFMT_LIN_A8R8G8B8, D3DPOOL_DEFAULT, &display, 0);

display->LockRect(0,&rect,NULL, NULL);

BYTE* pByte =(BYTE*)rect.pBits;

for (DWORD y=0;y<32;++y)
{
for (DWORD x=0;x<64;++x)
{
DWORD index = (x*4)+(y*rect.Pitch);
pByte[index] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+1] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+2] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+3] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
}
}

display->UnlockRect(0);

http://i18.photobucket.com/albums/b145/Powerslayer/IMG_2060.jpg

CHR15x94
January 31st, 2010, 00:43
Never mind. I think I've found my problem. Sorry for the waste of space. :P


- CHR15x94

Cabreak
February 27th, 2010, 21:27
Hi (New to the forum)

I've made a CHIP8 Emulator for Windows. I know there are a lot around here but I just want to share it with you all. I also provided the source code which is written in Borland Delphi 7.
Almost all opcodes are programmed, some are buggy.


Features:
-(Fully) emulates the Chip8. Including beeps ,collision,sprites ...
- Converts a Chip8 game to a Windows executable. :drool:
- A nice GUI
- Visual or normal keyboard
- Box graphics mode.
- Force acceleration (on slower PC's)
- Debug the game (Registers,timers ..)
- Manually choose the framerate (to slow down your game)


Planned features:
- Converts a Chip8 game to a Gamecube or Nintendo DS executable.
- Emulation for SCHIP


Link (Executable): http://www.di-martino.net/ChipsEmu-v1Beta.zip
Link (Source, Delphi 7): http://www.di-martino.net/ChipsEmu-v1-BETASOURCE.zip


PS: I hope this is the right place to post it.

Thank you
Cabreak (Di-Martino.net)

Marce1991
March 29th, 2010, 04:04
Yo guys, been a while since I posted here (6 years lol)...Well I'll make it fast:

I'm trying to code an 8chip emu, but first I'm testing the opcode reading process, cuz I'm a total newb in emulation, but I have some knowledge on cpp...
Here's what I have so far:


#include <cstdlib>
#include <iostream>
#include <fstream>

using namespace std;

ifstream::pos_type size;
char * Memory;
ifstream file ("PONG", ios::in|ios::binary|ios::ate);
int main(int argc, char *argv[])
{
unsigned int I;
unsigned int MemorySize; //How big is the ROM?
unsigned int CurrentOpcode;
Memory = new char [MemorySize]; //We will allocate memory dynamically for each ROM file.

size = file.tellg();
file.seekg (0, ios::beg);
file.read (Memory, MemorySize);
for(int i = 0;i< MemorySize; i++)
{
CurrentOpcode = Memory[i];
if ((CurrentOpcode& 0x00ff) == 0xe0)
{ cout << "AHA!!!!"; }
if ((CurrentOpcode& 0x00ff) == 0xee) { cout <<"WTH"; }
if (CurrentOpcode == 0x4) {cout << "WOW"; }
}
file.close();
system("PAUSE");
return EXIT_SUCCESS;
}

Is this working fine? Cuz I can't tell rofl, I do get some 'ahas' and 'wow's but I don't know, there's just too many things I don't understand at all...

You see, in this line:
if ((CurrentOpcode& 0x00ff) == 0xee) { cout <<"WTH"; }
is the '&' operand used for an AND operation here?
How do I compare the opcodes? I think they're 2 bytes long but I don't know how does that measure in a hex sentence. And why does it seem the emu's compare diferent sized mem. adresses like in the same sentence as cited above:
if ((CurrentOpcode& 0x00ff) == 0xee) { cout <<"WTH"; }
0x00ff is longer than 0xee, so are you comparing the first adress with only the 'start' of the 2nd?
Sooo I'm pretty sure my concepts are wrong, and that's why I'd like to know how emulators read and compare the adresses. The rest is simply defining functions, and setting delays, right??
Also my MemorySize is reaching an astoundly high number for PONG lol
I wonder if it's correct...
It reaches 4072505.
What does that mean? lol
Anyway, sorry for the one thousand questions...
Help from any kind is appreciated.
Basically, I just wanna know how the emulator read and compare memory adresses.

Thanks in advance!

Marce1991
March 30th, 2010, 17:37
K nvm, solved it on IRC lol

Marce1991
April 6th, 2010, 01:09
Hey guys, sorry for the third consecutive post, but now I have made tons of progress!!I have implemented nearly all the opcodes. But I came across some problems. Luckily, I think I managed to isolate them...
But first and foremost I would like you guys to evaluate way I made the CPU, it's unusual, but I think it's correct! (that's one of my biggest doubts)
Instead of writing these horrible "opcode&0xf000" structures, with array subscrits all over them, I wanted to make something perhaps a bit less efficient, but more 'readable' for me. (We're talking about chip 8 right? I'll have to slow it down, nevertheless)


void Hash( unsigned short int opcode, unsigned int n) {
//n ranging from 1 to 3
switch(n){
case 1:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0fff;
break;
case 2:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00ff;
break;
case 3:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00f0;
OpcodeHash[2] >>= 4;
OpcodeHash[3] = opcode & 0x000f;
break;
}
}

Exactly, I allowed myself to break the opcode into different patterns. Pattern 1 is A-BCD, Pattern 2 is A-B-CD, and Pattern 3 is A-B-C-D. And yes, that makes the opcodehash a global variable. (I wanted to make a triple return function but thats impossible in cpp).

Here's the basic emu structure, being developed under the Windows API:


char Memory[MemorySize];
unsigned int i = 0x200;

while ( true ) {
while (PeekMessage...== TRUE) {

do typical windows crap;
}
//my emu

FullOpcode = ((Memory[i] << 8) | (Memory[i + 1])) ;
//yes yes I'll fuse it, then break it well, at least it seems to work, but please tell me if this doesn't!

cout << FullOpcode << "\n";
//allows me to keep track of which opcode is being executed

Hash(FullOpcode, 1);
//The typical mass switch
switch(OpcodeHash[0]){
case 0x0:
//do opcode...
break;

case 0x1:

break;

.
.
.
}
i = i + 2; //---->Should I increment it twice? Memory[0xfff] is a char array, so I //suppose it has to be incremented twice since it records 1 byte per array index.
}

Well it seems to be working decently in the internal opcodes but some are clearly messed up...
One is certainly the stack...
It keeps getting overflown! I thought about managing the array's index, but that would make me waste some elements on the stack, is that ok?
Well without delay here's the stack code:

(This is the call sub opcode, I decrement it by two because at the very end of the code I increment the i twice... ( i+=2 ))


case 0x2:
if(SP <= 15){

SP++;
stack[SP] = i;
i = OpcodeHash[1] - 2; //Set it to NNN
}

Here's the return from sub, ALSO overflowing sometimes:
(the stack is inside the else structure)



switch(OpcodeHash[0]){
case 0x0:
Hash(FullOpcode, 3);
if(OpcodeHash[3] == 0x0) { //Clear the Screen
for(int a = 1;a<65;a++){
for(int b = 1;b<33;b++){
Pixels[a][b] = false;
}
}
break;
}
else {
//return from sub
i = stack[SP] - 2; //again, it will be incremented by 2 further on so...
SP--;
}

Another problem is my drawing never seems to work lol
I've made tons of tests and came to the conclusion that the mistake is somewhere inside the void DrawScene() function:
(I know it sucks balls, but please just tell me what's wrong with it)


void DrawScene(){
HDC hdc = GetDC(hwnd);
RECT rct[64][32];

for (int x = 1; x < 65; x++) {
for (int y = 1; y < 33; y++) {
if(Pixels[x][y] == 0) {

rct[x][y].left= x*5+ 65;
rct[x][y].right= x*5+ 60;
rct[x][y].top= y*5+ 65;
rct[x][y].bottom= y*5+ 60;
FillRect(hdc, &rct[x][y], blackBrush);
}
else
{

rct[x][y].left= x*5 + 65;
rct[x][y].right= x*5 + 60;
rct[x][y].top= y*5 + 65;
rct[x][y].bottom= y*5 + 60;
FillRect(hdc, &rct[x][y], whiteBrush);
}
}
}

}

As for the program output, well, it prints out opcodes, and then crashes when the stack overflows.

Finally, the full code, to any adventurer who dare dig through it (lol)


#include <cstdlib>
#include <iostream>
#include <fstream>
#include <time.h>
#include <windows.h>
#include <SDL.h> //I'm migrating to the windows API so this is temporary
#pragma comment( lib , SDL_image.lib )
#include "CPU_Timing.h"
#define MemorySize 0xfff
using namespace std;

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/* Make the class name into a global variable */
char szClassName[ ] = "8 Chip F.A.I.L.";

ifstream::pos_type size;
ifstream file ("PONG", ios::in|ios::binary);

void Hash( unsigned short int, unsigned int ); //variable to be hashed, number of 'cuts'
unsigned int Power( unsigned int, unsigned int );
unsigned int ConvertDecimalToBinary( unsigned int );

void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);
void DrawScene();

SDL_Event event;
//globals, I know you will want to kill me,
//but just lemme get this working, and I'll remove some of them
unsigned short OpcodeHash[3]; //we're breaking it in as many parts as needed
HBRUSH whiteBrush = CreateSolidBrush(RGB(255,255,255));
HBRUSH blackBrush = CreateSolidBrush(RGB(0,0,0));
HWND hwnd;
bool Pixels[64][32]; //1 for a white pixel 0 for a black pixel!


int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)

{

//HWND hwnd; /* Oops it had to become global for me to access it through
//the DrawScene();
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
srand((unsigned)time(NULL)); //Needed for the random numbers

unsigned short FullOpcode;
unsigned short X[7]; //8-bit
unsigned short Y[7]; //8-bit

unsigned int I; //memory crap
int SP = 0; //Stack Crap
unsigned int DT = 0; //Delay Timer
unsigned int ST = 0; //Sound Timer
unsigned int N; //drawing stuff
unsigned int V[0xf]; //0xf = 15 in hex...So gay to write it that way but wth
unsigned int key[0xf];
unsigned int stack[16];
int tempHash; //binary encoding
unsigned int A, B, C; //Also for the binary encoding

unsigned int xpos; //sprite xpos
unsigned int ypos; //sprite ypos

unsigned int i = 0x200; //we must start at the adress 0x200 in hex or 512 in dec



char Memory[MemorySize];

//init keys
for (int o = 0; o < 16; o++){ key[o] = 0; }

if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
return false;
}

/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);


/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */

//read rom
size = file.tellg();
file.seekg (0, ios::beg);
file.read (Memory, MemorySize);
printf("Rom loaded succesfully.");

/* Black background */
wincl.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));

/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;

/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"8 Chip F.A.I.L.", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
640, /* The programs width */
320, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);

/* Make the window visible on the screen */
ShowWindow (hwnd, nFunsterStil);


/* Run the message loop. It will run until GetMessage() returns 0 */
//while (GetMessage (&messages, NULL, 0, 0))
while (true)
{
start = clock(); //let the ticking begin!
//DrawScene(); Too buggy for me to put you on right now
while (PeekMessage(&messages, hwnd, 0, 0, PM_REMOVE) == TRUE) {

if (messages.message == WM_QUIT)
break;

/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}

if(i > MemorySize){
cout << "Memory Overflow!\n"; //lol wtf?
//system("PAUSE");
//i = 0x200;
}

//First fuse'em
FullOpcode = ((Memory[i] << 8) | (Memory[i + 1])) ;

cout << FullOpcode << "\n"; //keep track of the code





/*almost all opcodes have to be broken into one 4-bit part and
another 12-bit part, and then we decide if we break the 12-bit part or not
so... to the bit grinding!!*/


//Now hash once (don't forget flour and butter)
Hash(FullOpcode, 1);
//Switch!
switch(OpcodeHash[0]){
case 0x0:
Hash(FullOpcode, 3);
if(OpcodeHash[3] == 0x0) { //Clear the Screen
for(int a = 1;a<65;a++){
for(int b = 1;b<33;b++){
Pixels[a][b] = false;
}
}
break;
}
else {
//return from sub (wtf?)
i = stack[SP] - 2;
SP--;

}
break;
case 0x1: //Get ready to JUMP!

i = (OpcodeHash[1] - 2); //NNN - 2 (it will be incremented at the end of the loop)
break;

case 0x2:
if(SP <= 15){

SP++;
stack[SP] = i;
i = OpcodeHash[1] - 2; //Set it to NNN
}


break;

case 0x3:

Hash(FullOpcode, 2);
if( V[OpcodeHash[1]] == OpcodeHash[2] ) { //VX = NN?
i += 2; //skip
}
break;

case 0x4:

Hash(FullOpcode, 2);
if( V[OpcodeHash[1]] != OpcodeHash[2] ) { //VX != NN?
i += 2; //skip
}
break;

case 0x5:

Hash(FullOpcode, 3);
if( V[OpcodeHash[1]] == V[OpcodeHash[2]] ) { //VX = VY?
i += 2; //skip
}
break;

case 0x6:

Hash(FullOpcode, 2);
V[OpcodeHash[1]] = OpcodeHash[2]; //VX = NN
break;

case 0x7:

Hash(FullOpcode, 2);
V[OpcodeHash[1]] += OpcodeHash[2]; //VX = VX + NN
break;

case 0x8:

Hash(FullOpcode, 3);

switch(OpcodeHash[3]){

case 0:
V[OpcodeHash[1]] = V[OpcodeHash[2]]; //VX = VY
break;

case 1: //or
V[OpcodeHash[1]] = (V[OpcodeHash[1]] | V[OpcodeHash[2]]); //VX or VY
break;

case 2: //and
V[OpcodeHash[1]] = (V[OpcodeHash[1]] & V[OpcodeHash[2]]); //VX and VY
break;

case 3: //xor
V[OpcodeHash[1]] = (V[OpcodeHash[1]] ^ V[OpcodeHash[2]]); //VX xor VY
break;

case 4:
if((V[OpcodeHash[1]] & V[OpcodeHash[2]]) != 0) {
//That means that if we are to sum these numbers (binary),
//there will be at least one carry!
V[0xf] = 1;
}
else
{
V[0xf] = 0;
}

V[OpcodeHash[1]] = V[OpcodeHash[1]] + V[OpcodeHash[2]];
break;

case 5:
X[0] = V[OpcodeHash[1]] & 1;
Y[0] = V[OpcodeHash[2]] & 1;
if(Y[0] > X[0]){ //must be one luck guy
V[0xf] = 0;
}
else{
for(int a = 1; a < 7; a++){
X[a] = V[OpcodeHash[1]] & Power(2, a);
Y[a] = V[OpcodeHash[2]] & Power(2, a);
if (Y[a] > X[a]){ //there's the borrow!
V[0xf] = 0;
break;
}
else
{
V[0xf] = 1;
}
}
}
V[OpcodeHash[1]] = V[OpcodeHash[1]] - V[OpcodeHash[2]];
break;

case 6:
V[0xf] = (V[OpcodeHash[1]] & 1); //VF is the least significant bit of VX
V[OpcodeHash[1]] >>= 1; //Shifts VX by 1 (right)
break;

case 7:
X[0] = V[OpcodeHash[1]] & 1;
Y[0] = V[OpcodeHash[2]] & 1;
if(X[0] > Y[0]){ //must be one luck guy
V[0xf] = 0;
}
else{
for(int a = 1; a < 7; a++){
X[a] = V[OpcodeHash[1]] & Power(2, a);
Y[a] = V[OpcodeHash[2]] & Power(2, a);
if (X[a] > Y[a]){ //there's the borrow!
V[0xf] = 0;
break;
}
else
{
V[0xf] = 1;
}
}
}
V[OpcodeHash[1]] = V[OpcodeHash[2]] - V[OpcodeHash[1]];
break;

case 0xe:
V[0xf] = V[OpcodeHash[1]] & 128; //VF is set to the most significant bit of VX
V[OpcodeHash[1]] <<= 1; //Shifts VX by 1 (left)
break;
}

break;

case 0x9:
Hash(FullOpcode, 3);
if(V[OpcodeHash[1]] != V[OpcodeHash[2]]){ //VX != VY?
i += 2; //skip
}
break;

case 0xa:
I = OpcodeHash[1]; //I = NNN
break;

case 0xb:
i = OpcodeHash[1] + V[0] - 2; //jumps to NNN + V0...Has to be decremented because it will be
//incremented at the end of the code
break;

case 0xc:
Hash(FullOpcode, 2);
V[OpcodeHash[1]] = int((double(rand())/RAND_MAX)*255); //max of 8 bit
V[OpcodeHash[1]] = V[OpcodeHash[1]] & OpcodeHash[2];
break;

case 0xd: //yay, let's draw!!! But soon, because right now we can't even plot a pixel...
/* Hash(FullOpcode, 3);
xpos = V[OpcodeHash[1]];
ypos = V[OpcodeHash[2]];
N = OpcodeHash[3];
V[0xf] = 0; //initially we assume there's no collision
for (int z = 1; z < N + 1; z++){
for (int v = 1; v < 9; v++){
if (Pixels[z + xpos][v + ypos] = false){ //No pixel! Must draw!!
Pixels[z + xpos][v + ypos] = true;
}
else
{
//flip 'em
V[0xf] = 1; //There has been a collision! Still, we must (un)draw!!
Pixels[z + xpos][v + ypos] = false;
}
}
}
break; */

case 0xe:
Hash(FullOpcode, 3);
if (OpcodeHash[2] == 9){
if (key[V[OpcodeHash[1]]] == 1){
i += 2; //skip
}
}
else{
if (key[V[OpcodeHash[1]]] != 1){ //triple array subscrit ftw
//Same shit, different day
i += 2; //skip
}
}
break;

case 0xf: //long list...again!!!!
Hash(FullOpcode, 2);
switch(OpcodeHash[2]){
case 0x07:
V[OpcodeHash[1]] = DT; //VX = DT
break;

case 0x0A: //INPUT! =O More to add soon...
while (SDL_PollEvent(&event)) {
if (event.type == SDL_KEYDOWN){
switch (event.key.keysym.sym) {
case SDLK_UP:
key[0x8] = 1;
V[OpcodeHash[1]] = 0x8;

break;
case SDLK_DOWN:
key[0x2] = 1;
V[OpcodeHash[1]] = 0x2;
break;
case SDLK_LEFT:
key[0x4] = 1;
V[OpcodeHash[1]] = 0x4;
break;
case SDLK_RIGHT:
key[0x6] = 1;
V[OpcodeHash[1]] = 0x6;
break;
case SDLK_ESCAPE:
// return 0; //quits
break;

}

}
}
break;

case 0x18:
ST = V[OpcodeHash[1]]; //ST = VX
break;

case 0x1E:
I += V[OpcodeHash[1]]; //I = I + VX
break;

case 0x29: //TODO (Unimplemented)
break;

case 0x33: //BCD - Binary Coded Decimal (255) = (0010) (0101) (0101)
//
// 2 5 5
tempHash = V[OpcodeHash[1]];
A = (tempHash / 100); //hundred
tempHash = tempHash - A*100;
B = tempHash/10; //dozen
tempHash = tempHash - B*10; //unit
C = tempHash;
// Now that the number is split, we may encode it!
Memory[I] = ConvertDecimalToBinary(A);
Memory[I + 1] = ConvertDecimalToBinary(B);
Memory[I + 2] = ConvertDecimalToBinary(C);
break;
case 0x55:
for(int h = 0; h < OpcodeHash[1] + 1; h++){
Memory[I] = V[h];
I++;
}
break;
case 0x15:
DT = V[OpcodeHash[1]]; //DT = VX
break;
case 0x65:
for(int h = 0; h < OpcodeHash[1] + 1; h++){
V[h] = Memory[I];
I++;
}
break;
}
break;
}

i += 2; //Increment 2 bytes to the CPU.
end = clock();
cpuTime= (end-start)/ (CLOCKS_PER_SEC);
delay( 1000/60 - cpuTime ); //60 hertz! No matter what pc!!
//The above makes sure that to any PC, there will be a 1/60 second stop after each
//instruction. That makes the emu run @ 60hz. Also syncs with the delay timers.
DT = DT + 1; //Increment delay timer
ST = ST + 1; //Increment sound timer


}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;

}

/* This function is called by the Windows function DispatchMessage() */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
file.close();
return 0;
}
/*void Hash( unsigned short int opcode, unsigned int n) {
//n ranging from 1 to 3
switch(n){
case 1:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 4;
break;
case 2:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 12;
OpcodeHash[2] = opcode << 8;
OpcodeHash[2] >>= 8;
break;
case 3:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 12;
OpcodeHash[2] = opcode << 8;
OpcodeHash[2] >>= 12;
OpcodeHash[3] = opcode << 12;
OpcodeHash[3] >>= 12;
break;
}
}*/
void Hash( unsigned short int opcode, unsigned int n) {
//n ranging from 1 to 3
switch(n){
case 1:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0fff;
break;
case 2:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00ff;
break;
case 3:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00f0;
OpcodeHash[2] >>= 4;
OpcodeHash[3] = opcode & 0x000f;
break;
}
}
unsigned int Power( unsigned int power, unsigned int exponent ) {
int base = power;
int p = 1;
while(p < exponent) {
power *= base;
p++;
}
return power;
}
unsigned int ConvertDecimalToBinary( unsigned int decimal ){

int a = 0;
int b = 0;
int c = 0;
int d = 0;
unsigned int binary;
if (decimal & 1 == 1 ) { a = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { b = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { c = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { d = 1;}
decimal >>=1;
binary = a*1 + b*10 + c*100 + d*1000;
return binary;
}
void DrawScene(){
HDC hdc = GetDC(hwnd);
RECT rct[64][32];

for (int x = 1; x < 65; x++) {
for (int y = 1; y < 33; y++) {
if(Pixels[x][y] == 0) {

rct[x][y].left= x*5+ 65;
rct[x][y].right= x*5+ 60;
rct[x][y].top= y*5+ 65;
rct[x][y].bottom= y*5+ 60;
FillRect(hdc, &rct[x][y], blackBrush);
}
else
{

rct[x][y].left= x*5 + 65;
rct[x][y].right= x*5 + 60;
rct[x][y].top= y*5 + 65;
rct[x][y].bottom= y*5 + 60;
FillRect(hdc, &rct[x][y], whiteBrush);
}
}
}

}


Well that's all folks, thanks in advance!

rcvalle
June 2nd, 2010, 04:12
Hi,

I did a quick implementation of a CHIP-8 interpreter based on available documentation. I used ANSI C and the SDL library, and I took about 24 hours to complete. However, the keyboard inputs are not working and I do not have time to fix it.

It is available at:
http://github.com/rcvalle/chip8

Best regards,

royboyjoytoy2
June 19th, 2010, 15:57
What I am looking for is help with making a drawing method and fixing parts of my cpu emulation. I have spent about three hours trying to figure our how the drawing method works, but I wasn't able to get it emulated. I do have a 2D boolean array that does draw correctly to the screen (you can play with it if you want), all I need to do is figure out how to store/parse the data properly in the array. Some of my other methods might not be correct, If I could get help on this we could have a great applet-based Open Source CHIP-8 emulator :)

my current source code:
http://host-a.net/royboyjoytoy2/JavaChip8.zip/link.png (http://host-a.net/royboyjoytoy2/JavaChip8.zip)

a quick look at my Chip-8 CPU Emulation method:


public void doOpcode(int code) { //Chip8 CPU Emulator
if(chipVarDT!=0) {
chipVarDT -= 1;
}
if(chipVarST!=0) {
chipVarDT -= 1;
}
String cstring = Integer.toHexString(code);
boolean didOpcode = false;
//log("called:"+Integer.toHexString(lastCode)+"::"+Integer.toHexString(code));
//log("I"+Integer.toHexString(chipVarI));
switch(code & 0xF000) { //Check first bit
case 0x1000: //1NNN; Jumps to address NNN.
chipCount = (code & 0x0FFF);
didOpcode = true;
break;
case 0x2000: //2NNN; Calls subroutine at NNN.
chipCopyCount[cccAt] = chipCount;
cccAt += 1;
chipCount = (code & 0x0FFF);
didOpcode = true;
break;
case 0x3000: //3XNN; Skips the next instruction if VX equals NN.
if(chipReg[subBit(code, 2)]==(code & 0x00FF)) {
chipCount += 2;
}
didOpcode = true;
break;
case 0x6000: //6XNN; Sets VX to NN.
chipReg[subBit(code, 2)] = (code & 0x00FF);
didOpcode = true;
break;
case 0x7000: //7XNN; Adds NN to VX.
chipReg[subBit(code, 2)] += (code & 0x00FF);
didOpcode = true;
break;
case 0x8000: //8000; Math functions
switch(code & 0x000F) {
case 0: //8XY0; Sets VX to the value of VY.
chipReg[subBit(code, 2)] = subBit(code, 3);
break;
case 1: //8XY1; Sets VX to VX or VY.
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)] | chipReg[subBit(code, 3)];
break;
case 2: //8XY2; Sets VX to VX and VY.
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)] & chipReg[subBit(code, 3)];
break;
case 3: //8XY3; Sets VX to VX xor VY.
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)] ^ chipReg[subBit(code, 3)];
break;
case 4: //8XY4; Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
chipReg[0xF] = 0;
int value = subBit(code, 2) + subBit(code, 3);
if(value > 255) {
chipReg[0xF] = 1;
}
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)] + chipReg[subBit(code, 3)];
case 5: //8XY5; VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
chipReg[0xF] = 1;
if (chipReg[subBit(code, 2)] < chipReg[subBit(code, 3)]) {
chipReg[0xF] = 0;
}
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)] - chipReg[subBit(code, 3)];
break;
case 6: //8XY6; Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.
chipReg[0xF] = chipReg[((code & 0x0F00)>>8)] & 0x1 ;
chipReg[subBit(code, 2)] >>= 1;
case 7: //8XY7: Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
chipReg[0xF] = 1;
if (chipReg[subBit(code, 3)] < chipReg[subBit(code, 2)]) {
chipReg[0xF] = 0;
}
chipReg[subBit(code, 3)] = chipReg[subBit(code, 3)] - chipReg[subBit(code, 2)];
break;
case 0xE: //8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
chipReg[0xF] = chipReg[((code & 0x0F00))>>8]>>7;
chipReg[subBit(code, 2)] >>= 1;
break;
}
break;
case 0x9000: //9XY0; Skips the next instruction if VX doesn't equal VY.
if(chipReg[subBit(code, 2)]!=chipReg[subBit(code, 3)]) {
chipCount += 2;
}
didOpcode = true;
break;
case 0xA000: //ANNN; Sets I to the address NNN.
chipVarI = (code & 0x0FFF);
didOpcode = true;
break;
case 0xB000: //BNNN; Jumps to the address NNN plus V0.
chipCount = (code & 0x0FFF)+chipReg[0x0];
didOpcode = true;
break;
case 0xC000: //CXNN; Sets VX to a random number and NN.
chipReg[subBit(code, 2)] = (new Random()).nextInt() & (code & 0x00FF);
didOpcode = true;
break;
case 0xD000: //DXYN; Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a height of N pixels. As described above, VF is set to 1 if any screen pixels are flipped from set to unset when the sprite is drawn, and to 0 if that doesn't happen.
OpcodeDXYN(code);
case 0xE000: //Keyboard input
switch(code & 0x00FF) {
case 0x9E: //EX9E; Skips the next instruction if the key stored in VX is pressed.
if(true) {
chipCount += 2;
}
break;
case 0xA1: //EXA1; Skips the next instruction if the key stored in VX isn't pressed.
if(false) {
chipCount += 2;
}
break;
}
didOpcode = true;
break;
case 0xF000: //Various
switch(code & 0x00FF) {
case 0x07: //FX07; Sets VX to the value of the delay timer.
chipReg[subBit(code, 2)] = chipVarDT;
break;
case 0xA1: //FX0A; A key press is awaited, and then stored in VX.
chipReg[subBit(code, 2)] = 0xF;
case 0x15: //FX15; Sets the delay timer to VX.
chipVarDT = chipReg[subBit(code, 2)];
break;
case 0x18: //FX18; Sets the sound timer to VX.
chipVarST = chipReg[subBit(code, 2)];
case 0x1E: //FX1E; Adds VX to I.
chipReg[subBit(code, 2)] = chipReg[subBit(code, 2)]+chipVarI;
break;
case 0x29: //FX29; Sets I to the location of the sprite for the character in VX. Characters 0-F (in hexadecimal) are represented by a 4x5 font.
chipVarI = chipReg[subBit(code, 2)]*5;
break;
case 0x33: //FX33; Stores the Binary-coded decimal representation of VX at the addresses I, I plus 1, and I plus 2.
int value = chipReg[subBit(code, 2)];
int hundreds = value / 100;
int tens = (value / 10) % 10;
int units = value % 10;

chipReg[chipVarI] = hundreds;
chipReg[chipVarI+1] = tens;
chipReg[chipVarI+1] = units;
break;
}
didOpcode = true;
break;
}
switch(code) { //Check whole code
case 0x00E0: //00E0; Clears the screen.
chipPix = new boolean[64][32];
didOpcode = true;
break;
case 0x00EE: //00EE; Returns from a subroutine.
cccAt -= 1;
chipCount = chipCopyCount[cccAt];
didOpcode = true;
break;
}
if(!didOpcode) {
runCpu = false;
} else {
lastCode = code;
}
}

rcvalle
July 4th, 2010, 14:39
Hi royboyjoytoy2,

I fixed all the problems of the interpreter. You may want to look at chip8_pixels_draw() function in http://github.com/rcvalle/chip8/blob/master/chip8.c

Best regards,

io-grey
July 5th, 2010, 13:08
hi guys.
got a few questions.

-to which memory address do you load the game/rom data?

-at which address is the first instruction?
in cowgod's guide. it mentions 0x200 (most of the time) and 0x600 (sometimes for the ETI 660 computer). is there a way to determine the right address?

greetings

Tronix
August 6th, 2010, 15:34
Hi, all.

Today i done my Delphi CHIP-8 emulator. I added SCHIP support, fixed annoying bug with keyboard, rewrite draw function and more...

During testing, I wrote simple TEST program for (S)CHIP-8 emulators. This program contain 26 tests, including:
FX65 instruction test
Font 8x5 test
BCD instruction test
Math operation tests ADD,SUB,SHL,SHR,XOR (carry flag, borrow)
HP48 flags test
FX1E instruction (buffer overflow test)

Full assembler source code included in archive.

This is my Delphi emulator. All tests passed:
http://loadpicture.ru/images/ch1.png

And this David Winter CHIP8 emulator. Also all tests done:
http://loadpicture.ru/images/ch11.png

Some emulators from this topic:
http://loadpicture.ru/images/ch2njn.png
http://loadpicture.ru/images/ch3.png
http://loadpicture.ru/images/ch4.png
http://loadpicture.ru/images/ch5.png
http://loadpicture.ru/images/ch6.png
http://loadpicture.ru/images/ch8.png




SCTEST

Small programm for test (S)CHIP-8 emulators. If all test passed, you see
"OK" on upper left corner, else programm print ERROR and error number.
Written by Sergey Naydenov, e-mail: tronix286[dog]rambler.ru (c) 2010

Errors:

ERROR INI
Emulator initialization failed. When program start, all registers
must be set to null.

ERROR BCD
BCD instruction problems.

ERROR 0
Problems with Fx65 instruction. Can't load zeroes from memory to
registers.

ERROR 1
System font 8x5 not found. In memory at offset 000h - zeroes.

ERROR 2
Addiction without overflow (254+1). VF register need to be set 0,
but after operation he still 1

ERROR 3
After operation 254+1, register v0 need set to 255, but he does't.

ERROR 4
Addiction with overflow (255+1). VF register must be set to 1, but
after operation he still 0

ERROR 5
Wrong result after addiction operation (255+1). Must be 0.

ERROR 6
After substraction 1-1 register VF must be 1, but he still 0.

ERROR 7
Wrong result after 1-1 operation. Result must be 0.

ERROR 8
Substract 0-1. VF register must be 0, but he still 1

ERROR 9
Wrong result after 0-1 operation. Register v0 must be 255.

ERROR 10
... skip ...
..sorry, i don't have time for writing all descriptions. Please see
source code SCTEST.C8..

ERROR 23
Can not restore HP48 flags (FX75/FX85 instructions).

ERROR 24
Check FX1E (I = I + VX) buffer overflow. If buffer overflow, register
VF must be set to 1, otherwise 0. As a result, register VF not set to 1.
This undocumented feature of the Chip-8 and used by Spacefight 2019!
game.


I can't attach file to this forum (i get "cant' copy/move file" error message).
You can download this test by clicking on SCTEST_12.ZIP from here: http://rghost.ru/2304760

Tronix
August 13th, 2010, 17:05
Hello guys!

I apologize in advance for my English, because it's not my native language.

Recently i had a crazy idea - write a Pascal-like compiler for CHIP8.
So, i want to present you the alpha version of C8PASCAL :) My compiler (or translator, if you want) generate ASM listing, witch can be compiled using CHIPPER V2.11 by Christian Egeberg. Let me show you simple example program, written in C8PASCAL:



Program Example2;
{ C8PASCAL example - division without using DIV operator }

Var
q,x,y,a,b,counter : byte;

Begin
x := 20; {First number}
y := 4; {Second number. As result we can get X DIV Y}
q := 0; {begin calculate}
while x >= y do
Begin
a := x shr 1;
b := y;
counter := 1;
while a >= b do
begin
b := b shl 1;
counter := counter shl 1;
end;
x := x - b;
q := q + counter;
End;
Debug(0,0,q); {Print result X DIV Y}

x := 20; {First number}
y := 4; {Second number}
Debug(0,10,x div y); {Print result using build-in DIV operator}
end.


Other examples in archive. This is screenshot of second example program - "calculate square root", written in C8PASCAL too:
http://loadpicture.ru/images/hchip8.png

This example rendering Serpinski triangle:

http://loadpicture.ru/images/serpinski.png



CHIP8 Pascal (C8PASCAL)

Version: 0.3 alpha - Very simple and slowly, becouse it is Alpha.

Written by Sergey Naydenov (c) 2010
e-mail: tronix286[dog]rambler.ru

C8PASCAL translate Pascal source code into CHIP8 assembler listing,
which can be compiled using CHIPPER V2.11 by Christian Egeberg.

======================================== ===
1) RUN-TIME ERRORS. RANGE CHECKING. MEMORY.
======================================== ===
C8PASCAL not support runtime range checking. It's mean, you need
be very careful, when you operate with variables. Also, C8PASCAL do not
overflow checking. Let's look at example:
Var
a,b,c : Byte;
mas : Array [0..7]; {8 bytes length array}
d : Byte; {this variable comes immediately after the array}
Begin
A := 250;
B := 6;
C := A + B; {here we get overflow. }
{As a result C contain 0; not 256}
B := B - 6; {B = B - 6 = 0}
C := A div B; {Divide by zero. As a result - infinite loop or crash}
a := Mas[20]; {range overflow. As result, in var "A" - random data}
Mas[8] := 55; {range overflow. As result, in var "D" - 55}

CHIP8 has 3584 bytes RAM memory (#FFFh - #200h = #E000h). For
operating with variables C8PASCAL used own stack, located at the end of
the program. Typically, the stack must have 10-12 bytes free, but if
stack overflow, program crashed. Therefore it is desirable not to exceed
the program size of 3000-3200 bytes.

============
2) VARIABLES
============
C8PASCAL can operate with BYTE (unsigned integer 8-bit) type only. So, all
variables must be defined as Byte.
Ex.:
Var
i : Byte; {loop variable}
a,b,c : Byte; (* some variables *)

============
3) CONSTANTS
============
Constants are well used for sprites or initial values of variables. The
constants do not differ from the variables in terms of the main program.
You can change them in the course of the your program. Constants section
should be located before the variables section.
Ex:
Const
Sprite : Array [0..3] = (
#01100110, {Sprite 8x4. Two vertical}
#01100110, {lines}
#01100110,
#01100110
);
FirstNumber : Byte = 64; {simple Byte constant}
SecondNumber : Array [0..0] = ($40); {array with 1 element.}
{hex $40 = 64 dec}
Begin
Debug(10,10,FirstNumber); {draw 064 at X=10, Y=10}
FirstNumber := FirstNumber + SecondNumber[0];
{FirstNumber = 64+64=128}
Debug(40,10,FirstNumber); {draw 128 at X=40, Y=10)
DrawSprite(10,20,4,^Sprite[0]); {draw vert lines at X=10, Y=20}
End.

=========
4) ARRAYS
=========
This C8PASCAL version support one-dimensional arrays only. Arrays allways
starting with 0 (zero) to N. Each array element has Byte type. Maximum array
size is 255 bytes. You don't needed writing "of Byte;" after array
defination. If you type something like that: "mas : array [0..7] of Byte;"
you get an error.
Ex.:
Var
mas : Array [0..7]; {first sprite. Array size = 8 bytes}
temp : Array [10..15]; {ignore first number 10 and set array to}
{16 bytes long}

In program you can operate arrays with help "[" and "]" token.
Ex.:
Mas[0] := 2; {set first array element to 2}
a := Mas[i-1]; {let variable "a" contain "i-1" array element}
b := Mas[(a div 3)+15];
Warning! No range checking there. If you define array as "mas : array [0..7];"
and then try to do "a := mas[127];" you get random data in "a".

==================================
5) DIVISION AND MULTIPLY OPERATORS
==================================
Originaly CHIP8 don't have division and multiplication commands. C8PASCAL
software emulate this operation. Therefore, these operations very slowly.
If you want to divide or multiply by a power of two, please use SHR and
SHL operation.
Ex.:
a := a div b; {division}
a := a mod b; {the remainer of division}
a := c * b; {multiply}

================
6) SHIFTING BITS
================
In original CHIP8 SHR and SHL shifting bits by one. For compartable, C8PASCAL
support shifting by any number and generate "SHL VX, VY" instruction for
CHIPPER. As far as i know, all emulators ignore "VY" and shifting "VX" by one.
So, if you need shifting more than one bits, you need do SHR/SHL operation
more times.
Ex.:
a := a shr 1; {a = a div 2}
a := a shl 1; {a = a * 2}
a := a shr 4; {the same as "a shr 1". last argument (4) }
{ignored by many emulators}
a := (((a shr 1) shr 1) shr 1) shr 1; { a = a shr 4 }

==========
7) LOGICAL
==========
C8PASCAL support primitive logic only.
Ex.:
If a = b then {if a equal b}
If a <> b then {if a not equal b}
If a > b then {if a greater then b}
If a < b then {if a less then b}
If a >= b then {if a greter or equal b}
If a <= b then {if a less or equeal b}
If (a mod 2) = 0 then {if a odd then ...}

===========================
8) PROCEDURES AND FUNCTIONS
===========================
Not support functions (yet). Simple support for procedures, without
parameters.
Ex:
Var
a : byte;

Procedure InitVariable; {initialize A variable}
Begin
a := 255;
End;

Begin {MAIN programm}
InitVariable; {Call InitVariable procedure}
Debug(a); {Print 255}
End.

===========================
9) SPECIAL CHIP8 PROCEDURES
===========================

Procedure DrawSprite(X,Y, N : Byte; Sprite : Pointer);
Draw sprite at X,Y position, N lines. N must be constant.
Ex:
Var
i : Byte; {loop variable}
OneDot : Byte; {one dot mask. It's 8x1 sprite!}
Fill : Array [0..7]; {8x8 sprite}
Begin
OneDot := $80 {"1......." in binary. Its our one line sprite}
For i := 0 to 7 do
Fill[i] := #FF; {fill 8x8 sprite. "11111111" in binary}
DrawSprite(0,0,1,^OneDot); {draw 8x1 sprite (dot) in upper left corner}
DrawSprite(30,20,8,^Fill[0]); {draw filled rectangle at X=30, Y=20}
End.

Procedure DrawChar(X,Y, FntSize, Number : Byte);
Draw number symbol (from $0 to $F) at XY pos. If FntSize = 5 then draw 8x5
sprite and if FntSize = 10 draw s-chip 16x10 (8x10 in chip8 mode) sprite.
FntSize must be constant.
Ex:
Var
Num : Byte;
Begin
Num := 2;
DrawChar(10,10,5,Num); {Draw "2" on the screen}
End.

Procedure Debug(X, Y, Num : Byte);
Print decimal number at XY position.
Ex:
Var
Num : Byte;
Begin
Num := 64 div 2;
Debug(0,0,Num); {Print 032 at upper left corner}
End;

Procedure Cls;
Clear the screen.

Procedure SetHigh;
Set 128x64 SCHIP-8 mode.

Procedure SetLow;
Set 64x32 CHIP-8 mode.

Procedure Scroll_Left;
Scroll screen 4 pixels left.

Procedure Scroll_Right;
Scroll screen 4 pixels right.

Procedure Scroll_Down(N : Byte);
Scroll screen N lines down. N must be constant.

Function Random(N : Byte);
Get random number AND N.

Procedure Delay(N: Byte);
Set delay timer to N.

In next versions: ByteToBCD, LoadFont, etc...

========
10) TO DO
========

Bug fixing, Input support, Local variables, Functions, Procedure and Functions
parameters, standart library functions, integer (signed) variable,
code optimization, and much more...
If you want to join this project, please contact me by e-mail.

===========
11) HISTORY
===========
v0.1 alpha 07.08.2010
+ First alpha reliase. Basic functions.
v0.2 alpha 13.08.2010
+ Added constants support;
+ Added SetHigh, SetLow, Cls, Scroll_Left, Scroll_Right,
Scroll_Down(N:byte) system procedures;
+ Added to examples Serpinski triangle drawing programm
* First optimization for generated code. If possible, use free
registers with stack operations;
* Fix token parser - when variable begins from the reserved words,
compiler gave an error;
v0.3 alpha
+ Added Random(N) function, Delay(N) procedure;
+ Added stars demo example.
* Fix bug with arrays pointer.
* Fix REPEAT-UNTIL bug.


You can download C8PASCAL from here: http://rghost.ru/2341789 by clicking on C8PASCAL_03a.zip.

overclocked
September 4th, 2010, 16:42
Hi,

This is my first post here. It looks like some good chip-8 dudes have posted here so I'm going give my question a chance. I'm a C# programmer as a profession, but fiddles with hardware as a hobby. My last summer vacation project was a hardware chip-8 implemented in a single FPGA. I think it is one of the first of its kind so that's nice. It connects to a VGA-monitor for graphics and a Atari/Amiga-joystick for control.

Things are starting to work out quite nice but I have a timing problem that seems very hard to fix. The games I have tried out this far is:

BLITZ
BLINKY
SPACE INVADERS
MISSILE
TETRIS
HIDDEN

After reading up on timing I first locked the main frequency to approx. 400Hz (400 chip-8 instructions/second) and made the timer decrease every 1/60s. This works for some games but not all. Now I've embedded info for every game to get its own Hz(100-1000Hz) and also a divider to always lock the TimerRegister to approx. 60Hz regardless of main frequency. It still does not work well with for example HIDDEN.

Have somebody seen the same problem with software emulators? Is there somebody who made a table of settings that work well for all/some chip-8 games? Also when trying out different emulators with the game HIDDEN I've seen different behaviors with keyboard control. Should the cursor move one square at a time or move several squares at a time without requiring neutral keyboard value(no key pressed) in between.

Magnus

Tobiy
September 4th, 2010, 17:01
Most Chip-8 games work well with about 14 instructions/timerupdate (840 instructions/second).
The waitkey-instruction reset the key state before waiting so the cursor should move one square at a time.

Tobiy

overclocked
September 4th, 2010, 22:12
Thanks for the tip Tobiy, that was one of the "bugs" still left in the chip-8 core! Because of the way the hardware is connected, I now wait for NO keypress and then wait for the specific keypress on the FX0A-instruction. Seem to work great. Regarding the timing I try 840 inst/sec and it looks and works quite OK with all games I tried this far with the above keyboardfix.

Next up is sound output. Sound register and opcode-implementation already done but actual sound signal left to fix.

Then I will try to add SuperChip opcodes and graphics!

Some other glitches present is that BLITZ have two problems.

1) The original BLITZ always draw the skyscrapers one pixel too long which end up showing at the top of the screen making the plane run directly into these pixels and get "Game Over" right away. I've read some other emulators writers having problems with this one and as I understand it is a bug the the original code, but thanks to the original implementation not having "Sprite y-wrapping" the extra pixels never showed. I now use a fixed version of the ROM instead. Any thoughts about this?

2) Due to the fact that I'm using a static LFSR-register to implement the random instruction CXNN the skyline in BLITZ always start the same way. In my start sequence this means a skyscraper is drawn in the very left pixelrow (x=0). Is this normal? When the plane wraps around the X, the game automatically lower the plane 1 row in Y. This also mean that when trying to bomb the left-most skyscraper the plane now kills itself because the plane is lowered at the same time. How is this supposed to work? Anyone know anything, that would be great!

Thanks!

overclocked
September 21st, 2010, 18:43
Yo, I don't know how many users that actually reads this long thread but I'll keep adding questions..

Does anybody know about a really good and exact Chip-8 emulator implementation? As a step in building the first CHIP-chip-8 (that is the first implementation using logic gates), I though I would try out writing something in assembler myself. To be able to do this I needed a tool chain for Chip-8 assembler. I've chosen the original Chipper assembler and a great Chip-8 emulator written in JAVA and called JChip8BR.

The chosen chip-8 program is a Monitor/HexMemEditor. Its already working as a HexViewer and just some details left before it can be used to Edit HexValues in memory. BUT, the only emulator I've got that actually can run it is my fixed JChip8BR. Anyone got another GOOD one I could try?

Tronix, I tried your genius sctest program on the JChip8BR and found 3 bugs in the opcodes. After fixing those, the program shows "OK", so I think its probably quite OK now. Is the author listening? Great work you've done. I've fixed up some good updates for the emulator that I'm willing to Commit to GoogleHosting if I were to get membership in the project.
Some updates:
1) Done refactoring to seperate disassembly/execution of chip8-instructions. Switched the const int's in Instruction.java for a new enum eInstruction. This enum holds both the opcode-hexcode, the string and the bitmask used to identify it. It also holds the specific opcode-value. The num also includes a static method to convert from int to eInstruction.
2) New generic/configurable methods to format chip8-addresses/values.
3) Switched fonts from monospaced to fixed size which is much better when formatting source code output. Much better and more output in Memory window.
4) Added a Restart/Reset button/method which does a cold boot (loads the last used file from disk again, resets all registers)
5) Added command-line support for a single parameter consisting of a full path to a chip8-binary file. The emulator now initializes (but does not execute) directly when started.
6) Fixed some misspellings in the source code.
7) Added simple handling of Breakpoints, just double-click in the disassembly window to Toggle breakpoint.
8) Now possible to change register values any time, for example when doing debugging/stepping, even the PC (program counter) can be changed.

xdaniel_FWB
September 26th, 2010, 14:04
Dusting off my account here (last post over 4 years ago, heh), here's the next person to have written a Chip-8 interpreter:

http://imgur.com/6fpA7.pnghttp://imgur.com/d3wXO.png
http://imgur.com/FNsLD.pnghttp://imgur.com/py6b2.png

- Written in C, using SDL, SDL_ttf and SDL_gfx
- Appears to be compatible with every regular Chip-8 game I've thrown at it

Download (Win32 binary): http://code.google.com/p/ozmav/downloads/detail?name=Azunyan8_v1.rar

Usage is simple; if the interpreter is started without any command line arguments, you get a file selection dialog (see screenshot 1) which allows you to open a Chip-8 program. In-game, you can press F11 to have a look at the remaining controls, while pressing F12 shows the about box and ESC exits the program. Here's hoping I didn't forget anything...

I will probably be trying to build the program on Linux, as well as post the source code later :)

MicBeaudoin
September 27th, 2010, 04:41
Hello,

here is a little update of my old emu(Chiplol), it is now an upgraded version with a pseudo-3d rendering(change with Ctrl+2). I did it with two other people for a school project.

We compiled it for PSP and managed to... play online between a PSP and a PC! So useless lol(but funny)! It worked with the help of a java server, which I don't have anymore unfortunately.

It passes all tests in of SCTEST.
It's programmed in C++(the emu core is totally portable) with Qt for gui, that's why the download is a little bit big(big dlls)
Btw, it's all in french, sorry for this, I didn't translate it yet.
Here is a screenshot:
http://bayimg.com/FaPgMAACg

And the download(windows, about 3MB):http://rghost.net/2740686

Remote
January 28th, 2011, 22:47
Anyone made one of these in Python 3.x?

ihearpixels
January 29th, 2011, 04:52
Nope.

Or Ruby :P
Or even NASM/MASM based x86 ASM for the insane.......(wait, that gave me a idea....)

david winter
February 21st, 2011, 20:35
Hi there,

I thought I would never come back to my old 1990s MS-DOS Chip-8 emulator but my curiosity went too far. Luckily I picked an old CDROM with my sources. Got a headake to have MS-DOS booting from a floppy as I can't boot on a USB key. But I rapidly got sick of the old MS-DOS command line prompt and quickly wamped the whole thing from the hard drive.

Which brings me two questions for the heck of it...

1) What's the most accurate Windows-based emulator so far ? I'd like to play a few games for the old fun (both CHIP-8 and Schip).
2) If I had to port my sources to Windows for the challenge, what language would you recommend ?

Finally, are the original Cosmac-VIP games online ? I still have the old hex dumps on my CD and could post them here.
So far as I know, only one or two programs used a call to some 1802 machine code.

Any thoughts ? Thanks!

smcd
February 22nd, 2011, 00:58
Hi there,

I thought I would never come back to my old 1990s MS-DOS Chip-8 emulator but my curiosity went too far. Luckily I picked an old CDROM with my sources. Got a headake to have MS-DOS booting from a floppy as I can't boot on a USB key. But I rapidly got sick of the old MS-DOS command line prompt and quickly wamped the whole thing from the hard drive.

...

Any thoughts ? Thanks!

thought i'd derail the thread a bit and respond to the emboldened text- if it's because your bios doesn't support usb booting... never fear! i had an old laptop and put Plop boot manager on it and it works fine, albeit with a couple more steps than official bios support :) http://www.plop.at/en/bootmanager.html

hap
April 11th, 2011, 12:19
Ohh, nice to see a message from you here David. Thank you for your work and games, it helped many emulation enthusiasts get into emulator programming.

1) I honestly don't know, there are many many CHIP-8 emulators/interpreters around. I could brag that my implementation includes basic Cosmac VIP emulation (1802 cpu and all), but I won't. :P What I do want to brag about is the two games I made, Rush Hour for CHIP-8 and Sokoban for Super-chip, available with source code at my homepage: http://tsk-tsk.net/
2) ask yourself, what programming language are you good at/comfortable with?

Cosmac VIP games, I don't know, I do know that there are some hybrid CHIP-8/1802 games online. There's a program pack on http://chip8.com if you want to check it out.

_Zack_
January 28th, 2012, 23:07
It's available on iTunes for free :

http://itunes.apple.com/us/app/dreamchip/id492703600?mt=8

There is a chip8 emulator i wrote in lua using the Corona SDK for the iPhone (android version available tomorrow)

http://www.infuseddreams.com/wp-content/uploads/2012/01/mzl.odieuqtx.320x480-75.jpg

http://www.infuseddreams.com/wp-content/uploads/2012/01/mzl.eusbmywq.320x480-75.jpg

More info on this and other projects can be found on my site : http://www.infuseddreams.com/

Crckr Hckr
April 15th, 2012, 13:54
Can someone help me with mine, I get no output on the screen. Thanks.