Next Generation Emulation banner

201 - 220 of 486 Posts

·
Level 9998
Joined
·
9,384 Posts
Go go! :p 13 hours later and I have to remove the attachment because... my name was in the source code! Ahh... disaster! (or not!)

Anyway, I've opened a new thread in the emu news submission forums, and posted a new version there... without my name in the source code and with something nice: a fix for Spacefight 2019!

If you've tried that game on your emu, you'd know that... you will experience the infamous ship ghosting problem that seemingly has no cure! Well, I... got the cure! It seems to be the very last unimplemented and undocumented feature of the Chip-8 (note: it's a Chip-8 opcode). Here is what it is:

FX1E opcode: ADI - Add value of register VX into I

And here's the missing part: set VF if the result is a buffer overflow, or unset if it's not a buffer overflow. Buffer overflow occurs when the addition makes I > 0xfff.

In short, that opcode should be something like this:

V[0xf] = (I + V[x] > 0xfff) ? 1 : 0;
I = (I + V[x]) % 0x1000;

And... that's it, folks. The very last Chip-8 feature that is usually neglected because... not too many games would be that large in size, but... Spacefight 2019 was. Anyway, I hope that should conclude my quest for the perfect SuperChip emu now, unless... something else comes up. Hopefully not. :p
 

·
PReP - Lizard of Reason
Joined
·
870 Posts
Nice work with your emulator, have been following it for a while :)

Haha, about the name-slip up, are you afraid of the random psychopath that get's turned on by good code and would stalk you? :p
 

·
Emu author
Joined
·
1,488 Posts
Mmm... nope. But my employers might be able to find it somehow (by Googling!) and then I'd kind of be in trouble as I'm under licenses and stuffs. :p
You're not allowed to make a CHIP8 emulator? Are you sure about that? I wouldn't be very worried about such a thing. Unless you're under a contract that disallows you from making anything independently at all.
 

·
Level 9998
Joined
·
9,384 Posts
You're not allowed to make a CHIP8 emulator? Are you sure about that? I wouldn't be very worried about such a thing. Unless you're under a contract that disallows you from making anything independently at all.
Well, the latter. It's not like I'm not supposed to make anything independently, but anything I make during this period is supposed to be owned by my client. So to say.
 

·
Emu author
Joined
·
1,488 Posts
Ouch. I don't know who would write up something like that, it's pretty outrageous. Maybe there'll come a point where you can't be a chef without having to give all your home prepared meals to the restaurant.

Not that I can fault you for taking a job like that in this job climate. I'd have to be very desperate to go for something like that myself.
 

·
Level 9998
Joined
·
9,384 Posts
Well, I gotta help clear off some debts for my parents, so... anything that helps counts. :)

The extra I can make may be good, too. For higher studies and gadgets...
 

·
Site Owner
Joined
·
14,909 Posts
Ouch. I don't know who would write up something like that, it's pretty outrageous. Maybe there'll come a point where you can't be a chef without having to give all your home prepared meals to the restaurant.

Not that I can fault you for taking a job like that in this job climate. I'd have to be very desperate to go for something like that myself.

Don't worry, Rap makes extra cash selling prized swag. As for the project, its really a hellish scheme he is about to unleash on us all.
 

·
Registered
Joined
·
111 Posts
just decided to work on this using the alternate graphics method Hatorijr gave me in the other thread.

still having issues. but it's coming along.
 

·
Registered
Joined
·
7 Posts
wow I havnt been to ngemu since 2002 :)

I was wondering if anyone could help out with some trouble I'm having that I assume is down to the drw opcode.

Basically sprites arnt being erased properly; for example in Invaders, the title screen and scrolling text is drawn perfectly yet the alien ships are not erased completely when they move (some pixels are and others are not)

My drw code is (not yet optimized ;)):

Code:
private void drw_vx_vy_nibble()
		{
			Opcode.SetMnemonic("DRW V{0:X1}, V{1:X1}, #{2:X1}", Opcode.X, Opcode.Y, Opcode.N);
			
			if (DisassembleEnabled)
			{
				return;
			}
			
			RAMStream.Seek(Registers.I);
			
			byte[] sprite = RAMStream.ReadBytes((ushort) Opcode.N);
			
			/*
			 * Be nice and return to PC
			 */
			 
			RAMStream.Seek(Registers.PC);
			
			Registers.V[0xF] = 0;

			for (byte a = 0; a < sprite.Length; a++)
			{
				bool[] pixel = new bool[8];

				pixel[0] = ((sprite[a] & Convert.ToByte("10000000", 2)) > 0);
				pixel[1] = ((sprite[a] & Convert.ToByte("01000000", 2)) > 0);
				pixel[2] = ((sprite[a] & Convert.ToByte("00100000", 2)) > 0);
				pixel[3] = ((sprite[a] & Convert.ToByte("00010000", 2)) > 0);
				pixel[4] = ((sprite[a] & Convert.ToByte("00001000", 2)) > 0);
				pixel[5] = ((sprite[a] & Convert.ToByte("00000100", 2)) > 0);
				pixel[6] = ((sprite[a] & Convert.ToByte("00000010", 2)) > 0);
				pixel[7] = ((sprite[a] & Convert.ToByte("00000001", 2)) > 0);

				for (byte b = 0; b < 8; b++)
				{
					byte x = (byte) ((Registers.V[Opcode.X] + b > 64) ? 0 : Registers.V[Opcode.X] + b);
					byte y = (byte) ((Registers.V[Opcode.Y] + a > 32) ? 0 : Registers.V[Opcode.Y] + a);
					
					if(VideoDriver.GetPixel(x, y))
					{
						Registers.V[0xF] = 1;
					}

					VideoDriver.SetPixel(x, y, VideoDriver.GetPixel(x, y) ^ pixel[b]);
				}
			}

			VideoDriver.Render();
		}
and the video functions:

Code:
public override bool GetPixel(int x, int y)
		{
			return (surface.GetPixel(new Point(x, y)) == Color.White);
		}
		
		public override void SetPixel(int x, int y, bool on)
		{
			Color color = (on) ? Color.White : Color.Black;	
			surface.SetPixels(new Point(x, y), new Color[1, 1] { { color } });
		}
I've been picking my brains on this for a few days now and can't find an answer anywhere else. Any help greatly appreciated :) (C# & SDL btw ;))
 

·
Emu Author
Joined
·
613 Posts
before starting i beg you to switch from sdl for c#, its a bad memory hog if you sit and watch the memory use(it was 10 times worse when i did space invaders).

also you need to be drawing both black and white pixels, did not spend much time looking over your code but try that and see if it helps.
 

·
Registered
Joined
·
111 Posts
well I finally figured out the issues with chip 8.

Still not sure what the printf thing is about but yeah, chip8 works now.

just have to set it so that it actually expands to fill up the entire texture in chip8 mode.
 

·
Registered
Joined
·
7 Posts
before starting i beg you to switch from sdl for c#, its a bad memory hog if you sit and watch the memory use(it was 10 times worse when i did space invaders).

also you need to be drawing both black and white pixels, did not spend much time looking over your code but try that and see if it helps.
I found the problem after much hairpulling and it was down to a slight oversight on my part with sdl.net

A color returned by Surface.GetPixel doesn't match exactly the System.Drawing color; the name member is missing so

Code:
surface.GetPixel(x, y) == Color.White
will always evaluate to false. The solution was:

Code:
surface.GetPixel(x, y).ToArgb() == Color.White.ToArgb()

Will definately be looking into using a seperate toolkit, XNA sounds interesting...
 

·
Emu Author
Joined
·
613 Posts
yea that was another reason why i hated sdl, it got real bad for me at times. i say learn directx for c#, or do xna but managed directx is a bit easier to do emulators with than xna i would think, though i have a lot of experience with both.
 

·
Banned
Joined
·
221 Posts
someone here can explain the codes in the debugger ? only a line for example :
V0 40 0x28 / 0x0200 1217 JP535 what means all this addres??
i´m a low dev and i want to learn to code in emus, please give me a litle hand
 

·
Premium Member
Joined
·
8,201 Posts
what debugger is that taken from?

V0 --> Register identifier
40 --> Value in that register
0x28 --> Same value but in HEX (0x28 == 40)
0x0200 --> Most likely the PC. This is the address we're executing.
1217 --> The 2 bytes opcode value at the address above (jump to 0x0217)
JP535 --> What the opcode actually does... JP (jump) instuction. (535 is 0x0217 in DEC))
 

·
Registered
Joined
·
2 Posts
I am attempting to make a Chip8 Emulator using XNA... it works to a point, but the graphics do not draw right.... and I am about 80% sure all the other opp codes are working correctly, so the way I collect my info must not work right...

CPU:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CCE
{
    public class Chip8CPU
    {
        public byte[] V;
        public byte[] Memory;
        public UInt16 Opcode = 0, I = 0, PC = 0x200;
        public Stack<UInt16> OldPC;

        public byte DelayTimer;
        public byte SoundTimer;

        public Chip8CPU()
        {
            V = new byte[16];
            Memory = new byte[0xFFF];
            OldPC = new Stack<UInt16>(0x10);

            for (int i = 0; i < 80; i++)
                Memory[i] = Font[i];
        }

        public void UpdateTimers(Game1 game)
        {
            if (DelayTimer > 0)
                DelayTimer--;

            if (SoundTimer > 0)
                SoundTimer--;

            if (SoundTimer > 0)
                Beep(game);
        }

        private void Beep(Game1 game)
        {
            game.debugStream.WriteLine("-- BEEP!");
        }

        public void Run(Game1 game)
        {
            Opcode = Convert.ToUInt16((Memory[PC] << 8) + Memory[PC + 1]);

            game.oppStream.WriteLine(@"PC: {0}[{1}] ML: {2}     MH: {3}     Op: {4}[{5}]",
                PC, Convert.ToString(PC, 16),
                Convert.ToString(Memory[PC] << 8, 16),
                Convert.ToString(Memory[PC + 1], 16),
                Opcode.ToString("####"), Convert.ToString(Opcode, 16));

            switch (Opcode & 0xF000)
            {
                case 0x0000:
                    {
                        switch (Opcode & 0x000F)
                        {
                            case 0x0000: //Clear Screen
                                {
                                    game.pixels = new Dictionary<int, Dictionary<int, byte>>();
                                    game.graphics.GraphicsDevice.Clear(game.BackgroundColor);
                                    break;
                                }
                            case 0x000E: //Returns from Sub-Rutine
                                {
                                    PC = OldPC.Pop();
                                    break;
                                }
                        }
                        PC += 2;
                        break;
                    }
                case 0x1000: //Jump to Address NNN
                    {
                        PC = Convert.ToUInt16(Opcode & 0x0FFF);
                        break;
                    }
                case 0x2000: //Calls Sub-Rutine @ NNN
                    {
                        OldPC.Push(PC);
                        PC = Convert.ToUInt16(Opcode & 0x0FFF);
                        break;
                    }
                case 0x3000: //Skips next Instruction if VX == VN
                    {
                        if (V[Opcode & 0x0F00 >> 8] == Convert.ToByte(Opcode & 0x00FF))
                            PC += 4;
                        else
                            PC += 2;

                        break;
                    }
                case 0x4000: //Skips next Instruction if VX != VN
                    {
                        if (V[Opcode & 0x0F00 >> 8] != Convert.ToByte(Opcode & 0x00FF))
                            PC += 4;
                        else
                            PC += 2;

                        break;
                    }
                case 0x5000: //Skips next Instruction if VX == VY
                    {
                        if (V[Opcode & 0x0F00 >> 8] == Convert.ToByte(V[Opcode & 0x00F0 >> 4]))
                            PC += 4;
                        else
                            PC += 2;

                        break;
                    }
                case 0x6000: //Sets VX to NN
                    {
                        V[Opcode & 0x0F00 >> 8] = Convert.ToByte(Opcode & 0x00FF);
                        PC += 2;
                        break;
                    }
                case 0x7000: //Adds NN to VX
                    {
                        V[Opcode & 0x0F00 >> 8] += Convert.ToByte(Opcode & 0x00FF);
                        PC += 2;
                        break;
                    }
                case 0x8000:
                    {
                        switch (Opcode & 0x000F)
                        {
                            case 0x0000: //Sets VX to VY
                                {
                                    V[Opcode & 0x0F00 >> 8] = V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0001: //Sets VX to VX OR VY
                                {
                                    V[Opcode & 0x0F00 >> 8] |= V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0002: ////Sets VX to VX & VY
                                {
                                    V[Opcode & 0x0F00 >> 8] &= V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0003: //Sets VX to VX OR VY
                                {
                                    V[Opcode & 0x0F00 >> 8] ^= V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0004: //Adds VY to VX, VF is set to 1 when there is a carry and 0 when there isn't
                                {
                                    if ((V[Opcode & 0x0F00 >> 8] += V[Opcode & 0x00F0 >> 4]) > 255)
                                        V[0xF] = 1;

                                    V[Opcode & 0x0F00 >> 8] += V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0005: //Subtracts VY from VX, VF is set to 0 when there is a borrow and 1 when there isn't
                                {
                                    if ((V[Opcode & 0x0F00 >> 8] -= V[Opcode & 0x00F0 >> 4]) < 0)
                                        V[0xF] = 1;

                                    V[Opcode & 0x0F00 >> 8] -= V[Opcode & 0x00F0 >> 4];
                                    break;
                                }
                            case 0x0006: //Shifts VX right by one, VF is set to least significant bit of VX BEFORE shift
                                {
                                    V[0xF] = Convert.ToByte((V[Opcode & 0x0F00 >> 8]) & 0x1);
                                    V[Opcode & 0x0F00 >> 8] >>= 1;
                                    break;
                                }
                            case 0x0007: //Sets VX to VY - VX, VF is set to 0 when there is a borow and 1 when there isn't
                                {
                                    if ((V[Opcode & 0x00F0 >> 4] - V[Opcode & 0x0F00 >> 8]) < 0)
                                        V[0xF] = 1;

                                    V[Opcode & 0x0F00 >> 8] = Convert.ToByte(V[Opcode & 0x00F0 >> 4] - V[Opcode & 0x0F00 >> 8]);
                                    break;
                                }
                            case 0x000E: //Shifts VX left by one, VF is set to most signinficant bit of VX BEFORE shift
                                {
                                    V[0xF] = Convert.ToByte((V[Opcode & 0x0F00] >> 8) & 0x80);
                                    (V[Opcode & 0x0F00 >> 8]) <<= 1;
                                    break;
                                }
                        }
                        PC += 2;
                        break;
                    }
                case 0x9000: //Skips the next Instruction if VX != VY
                    {
                        if (V[Opcode & 0x0F00 >> 8] != V[Opcode & 0x00F0 >> 4])
                            PC += 4;
                        else
                            PC += 2;

                        break;
                    }
                case 0xA000: //Sets I to the address NNN
                    {
                        I = Convert.ToUInt16(Opcode & 0x0FFF);
                        PC += 2;
                        break;
                    }
                case 0xB000: //Jumps to address NNN + V0
                    {
                        PC = Convert.ToUInt16((Opcode & 0x0FFF) + V[0]);
                        break;
                    }
                case 0xC000: //Sets VX to a random number & NN
                    {
                        Random r = new Random();
                        V[Opcode & 0x0F00 >> 8] = Convert.ToByte(r.Next(0x0, 0xFFF) + (Opcode & 0x00FF));
                        PC += 2;
                        break;
                    }
                case 0xD000: //Draws a sprite at Coordinate(VX, VY) that has a width of 8 pixels and a height of N pixels
                    //When Pixel if flipped VF is set to 1 and 0 when it isn't
                    {
                        int scale = 10;
                        byte sX = Convert.ToByte(V[Opcode & 0x0F00 >> 8]);
                        byte sY = Convert.ToByte(V[Opcode & 0x00F0 >> 4]);
                        byte SpriteHeight = Convert.ToByte(Opcode & 0x000F);

                        for (int yline = 0; yline < SpriteHeight; yline++)
                        {
                            byte Data = Memory[I + yline];
                            for (int xpixel = 0; xpixel < 8; xpixel++)
                            {
                                if ((Data & (0x80 >> xpixel)) != 0)
                                {
                                    int x = sX + (xpixel * scale);
                                    int y = sY + (yline * scale);

                                    if (game.pixels.ContainsKey(x))
                                    {
                                        if (game.pixels[x].ContainsKey(y))
                                            game.pixels[x][y] ^= 1;
                                        else
                                            game.pixels[x].Add(y, 1);
                                    }
                                    else
                                    {
                                        Dictionary<int, byte> newy = new Dictionary<int, byte>();
                                        newy.Add(y, 1);
                                        game.pixels.Add(x, newy);
                                    }

                                    game.debugStream.WriteLine(
                                        "-- SWITCH FLAG | DATA  " +
                                        "BaseXY[{0},{1}] : " +
                                        "PixelXY[{2},{3}] : " +
                                        "FinalXY[{4},{5}] : " +
                                        "Point<{6}>  " +
                                        "FLAG: {7}",
                                        sX, sY,
                                        xpixel, yline,
                                        x, y,
                                        sX + xpixel + (sY + yline) * 64,
                                        game.pixels[x][y]);
                                }
                            }
                        }

                        PC += 2;
                        break;
                    }
                case 0xE000:
                    {
                        switch (Opcode & 0x00FF)
                        {
                            case 0x009E: //Skips the next instruction if the key in VX is pressed
                                {
                                    if (game.KeyStates[V[(Opcode & 0x0F00) >> 8]] > 0)
                                        PC += 4;
                                    else
                                        PC += 2;
                                    break;
                                }
                            case 0x00A1: //Skips the next instruction if the key in VX isn't pressed
                                {
                                    if (game.KeyStates[V[(Opcode & 0x0F00) >> 8]] == 0)
                                        PC += 4;
                                    else
                                        PC += 2;
                                    break;
                                }
                        }
                        break;
                    }
                case 0xF000:
                    {
                        switch (Opcode & 0x00FF)
                        {
                            case 0x0007: //Sets VX to the value of the Delay Timer
                                {
                                    V[(Opcode & 0x0F00) >> 8] = DelayTimer;
                                    break;
                                }
                            case 0x000A: //A key press is awaited then stored in VX
                                {
                                    bool submited = false;
                                    for (byte i = 0; i < game.KeyStates.Length; i++)
                                    {
                                        if (game.KeyStates[i] == 1)
                                        {
                                            submited = true;
                                            V[(Opcode & 0x0F00) >> 8] = game.KeyStates[i];
                                        }
                                    }

                                    if (!submited)
                                        PC -= 2;

                                    break;
                                }
                            case 0x0015: //Sets the Delay Timer to VX
                                {
                                    DelayTimer = V[(Opcode & 0x0F00) >> 8];
                                    break;
                                }
                            case 0x0018: //Sets the Sound Timer to VX
                                {
                                    SoundTimer = V[(Opcode & 0x0F00) >> 8];
                                    break;
                                }
                            case 0x001E: //Adds VX to I
                                {
                                    I += V[Opcode & 0x0F00 >> 8];
                                    break;
                                }
                            case 0x0029: //Sets I to the location of the sprite for the character in VX
                                {
                                    I = Convert.ToUInt16(V[Opcode & 0x0F00 >> 8] * 5);
                                    break;
                                }
                            case 0x0030://Sets I to the location of the sprite for the character in VX
                                {
                                    I = Convert.ToUInt16(V[Opcode & 0x0F00 >> 8] * 10);
                                    break;
                                }
                            case 0x0033: //Stores the binary coded decimal in VX at the address I, I + 1, I + 2
                                {
                                    Memory[I] = Convert.ToByte(V[(Opcode & 0x0f00) >> 8] / 100);
                                    Memory[I + 1] = Convert.ToByte((V[(Opcode & 0x0f00) >> 8] / 10) % 10);
                                    Memory[I + 2] = Convert.ToByte((V[(Opcode & 0x0f00) >> 8] % 100) % 10);
                                    break;
                                }
                            case 0x0055: //Stores V0 to VX starting at I
                                {
                                    for (int i = 0; i < ((Opcode & 0x0F00) >> 8); i++)
                                        Memory[I + i] = V[i];

                                    break;
                                }
                            case 0x0065: //Fills V0 to VX from memory starting at I
                                {
                                    for (int i = 0; i < ((Opcode & 0x0F00) >> 8); i++)
                                        V[i] = Memory[I + i];

                                    break;
                                }
                            default:
                                {
                                    game.oppStream.WriteLine("  -- OPP NOT IMPLEMENTED -- {0}[{1}]", Opcode, Convert.ToString(Convert.ToInt32(Opcode), 16));
                                    break;
                                }
                        }

                        PC += 2;
                        break;
                    }
                default:
                    {
                        game.oppStream.WriteLine("  -- OPP NOT IMPLEMENTED -- {0}[{1}]", Opcode, Convert.ToString(Convert.ToInt32(Opcode), 16));
                        PC += 2;
                        break;
                    }
            }
        }

        byte[] Font = new byte[] 
        {
            0xF0, 0x90, 0x90, 0x90, 0xF0,// 0 Num
            0x20, 0x60, 0x20, 0x20, 0x70,// 1 Num	
            0xF0, 0x10, 0xF0, 0x80, 0xF0,// 2 Num
            0xF0, 0x10, 0xF0, 0x10, 0xF0,// 3 Num
            0x90, 0x90, 0xF0, 0x10, 0x10,// 4 Num
            0xF0, 0x80, 0xF0, 0x10, 0xF0,// 5 Num
            0xF0, 0x80, 0xF0, 0x90, 0xF0,// 6 Num
            0xF0, 0x10, 0x20, 0x40, 0x40,// 7 Num
            0xF0, 0x90, 0xF0, 0x90, 0xF0,// 8 Num
            0xF0, 0x90, 0xF0, 0x10, 0xF0,// 9 Num
            0xF0, 0x90, 0xF0, 0x90, 0x90,// A Num
            0xE0, 0x90, 0xE0, 0x90, 0xE0,// B Num
            0xF0, 0x80, 0x80, 0x80, 0xF0,// C Num
            0xE0, 0x90, 0x90, 0x90, 0xE0,// D Num
            0xF0, 0x80, 0xF0, 0x80, 0xF0,// E Num
            0xF0, 0x80, 0xF0, 0x80, 0x80,// F Num
        };
    }
}
Main:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace CCE
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        public GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Rectangle viewportRect;

        public Dictionary<int, Dictionary<int, byte>> pixels;
        public Chip8CPU CPU;
        public byte[] KeyStates;

        public Color SpriteColor = Color.Gray;
        public Color BackgroundColor = Color.Black;

        string GameFile = String.Empty;
        System.IO.FileStream gameStream;

        public System.IO.StreamWriter oppStream;
        public System.IO.StreamWriter debugStream;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            this.Exiting += new EventHandler(Game1_Exiting);
            debugStream = new System.IO.StreamWriter("C:/debug.txt");
            oppStream = new System.IO.StreamWriter("C:/opps.txt");

            base.Initialize();
        }

        void Game1_Exiting(object sender, EventArgs e)
        {
            debugStream.Close();
            oppStream.Close();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            viewportRect = new Rectangle(0, 0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);
            pixels = new Dictionary<int, Dictionary<int, byte>>();
            CPU = new Chip8CPU();
            KeyStates = new byte[16];

            while (!GetGame())
                GetGame();

            LoadGame();
        }

        public bool GetGame()
        {
            System.Windows.Forms.OpenFileDialog fileDialog = new System.Windows.Forms.OpenFileDialog();
            fileDialog.DefaultExt = "Chip8 (.ch8) | *.ch8 | All Files | *.*";
            if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                GameFile = fileDialog.FileName;
                gameStream = new System.IO.FileStream(GameFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            }

            if (System.IO.File.Exists(GameFile))
                return true;

            return false;
        }

        public void LoadGame()
        {
            System.IO.BinaryReader reader = new System.IO.BinaryReader(gameStream);
            reader.Read(CPU.Memory, 0x200, Convert.ToInt32(reader.BaseStream.Length));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            if (Keyboard.GetState().IsKeyDown(Keys.NumPad7))
                KeyStates[1] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad8))
                KeyStates[2] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad9))
                KeyStates[3] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad4))
                KeyStates[4] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad5))
                KeyStates[5] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad6))
                KeyStates[6] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad4))
                KeyStates[7] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad2))
                KeyStates[8] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad3))
                KeyStates[9] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.NumPad0))
                KeyStates[0xA] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.OemPeriod))
                KeyStates[0] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.Enter))
                KeyStates[0xB] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.Divide))
                KeyStates[0xC] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.Multiply))
                KeyStates[0xD] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.Subtract))
                KeyStates[0xE] = 1;
            else if (Keyboard.GetState().IsKeyDown(Keys.Add))
                KeyStates[0xF] = 1;
            else
            {
                for (int i = 0; i < KeyStates.Length; i++)
                {
                    KeyStates[i] = 0;
                }
            }

            //for (int i = 0; i < 5; i++)
            {
                CPU.Run(this);
                CPU.UpdateTimers(this);
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(BackgroundColor);

            // TODO: Add your drawing code here
            spriteBatch.Begin();

            foreach (KeyValuePair<int, Dictionary<int, byte>> xpix in pixels)
            {
                Dictionary<int, byte> ylines = pixels[xpix.Key];
                foreach (KeyValuePair<int, byte> ypix in ylines)
                {
                    byte switched = ylines[ypix.Key];
                    Texture2D pixel = new Texture2D(graphics.GraphicsDevice, 9, 9, 1, TextureUsage.None, SurfaceFormat.Color);
                    Color[] pixelColors = new Color[pixel.Width * pixel.Height];
                    for (int i = 0; i < pixelColors.Length; i++)
                        pixelColors[i] = (switched == 1 ? SpriteColor : BackgroundColor);

                    pixel.SetData(pixelColors);
                    spriteBatch.Draw(pixel, new Vector2(xpix.Key, ypix.Key), Color.White);
                    debugStream.WriteLine("-- DRAW AT XY ({0},{1})", xpix.Key, ypix.Key);
                }
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
If I could get the graphics to draw right-ish i could probably fix all the other stuff... Anyways, "how are the graphics messed up?" they tile diagonally down and to the right.... or it simply only displays a tiny line or block in the upper left corner of the game...

ALSO, it seems to only want to draw pixels in a 5x9 area of blocks... (each block being 9x9 pixels)...

thanks for any help!

- Z -
 

·
Emu author
Joined
·
1,488 Posts
I didn't check the whole thing yet, but you made a mistake on this opcode:

Code:
                           case 0x000E: //Shifts VX left by one, VF is set to most signinficant bit of VX BEFORE shift
                               {
                                   V[0xF] = Convert.ToByte((V[Opcode & 0x0F00] >> 8) & 0x80);
                                   (V[Opcode & 0x0F00 >> 8]) <<= 1;
                                   break;
                               }
You put a shift by 8 outside of the array indexing.
 

·
Premium Member
Joined
·
8,201 Posts
I've actually ported my emulator to XNA also. The code is Zune specific however since I'm running it off my Zune HD.

I'll post the source up once I get on my laptop.
 

·
You're already dead...
Joined
·
5,472 Posts
any pics of it running on your Zune HD?

its always cool to see emus running on portable hw :D
 
201 - 220 of 486 Posts
Top