예제 #1
0
 /// <summary>
 /// Skips the next instruction (two bytes) if V[x] != nn.
 /// </summary>
 void SkipIfXNotEqual(OpCodeData data)
 {
     if (V[data.X] != data.NN)
     {
         PC += 2;
     }
 }
예제 #2
0
 // Misc has its own dictionary because it's full of random stuff.
 void Misc(OpCodeData data)
 {
     if (opCodesMisc.ContainsKey(data.NN))
     {
         opCodesMisc[data.NN](data);
     }
 }
예제 #3
0
 /// <summary>
 /// Skips the next instruction (two bytes) if V[x] != V[y].
 /// </summary>
 void SkipIfXNotEqualY(OpCodeData data)
 {
     if (V[data.X] != V[data.Y])
     {
         PC += 2;
     }
 }
예제 #4
0
 /// <summary>
 /// Saves all registers to the address in register I.
 /// </summary>
 void SaveX(OpCodeData data)
 {
     for (var i = 0; i <= data.X; i++)
     {
         RAM[I + i] = V[i];
     }
 }
예제 #5
0
 /// <summary>
 /// Skips the next instruction (two bytes) if V[x] == nn.
 /// </summary>
 void SkipIfXEqual(OpCodeData data)
 {
     if (V[data.X] == data.NN)
     {
         PC += 2;
     }
 }
예제 #6
0
 /// <summary>
 /// Loads all registers from the address in register I.
 /// </summary>
 void LoadX(OpCodeData data)
 {
     for (var i = 0; i <= data.X; i++)
     {
         V[i] = RAM[I + i];
     }
 }
예제 #7
0
 /// <summary>
 /// Skips the next instruction based on the key at V[x] being pressed/not pressed.
 /// </summary>
 void SkipOnKey(OpCodeData data)
 {
     if (
         (data.NN == 0x9E && pressedKeys.Contains(V[data.X])) ||              // 9E = IfKeyPressed
         (data.NN == 0xA1 && !pressedKeys.Contains(V[data.X]))                    // A1 = IfKeyNotPressed
         )
     {
         PC += 2;
     }
 }
예제 #8
0
        /// <summary>
        /// Draws an n-byte sprite from register I at V[x], V[y]. Sets V[0xF] if it collides.
        /// </summary>
        void DrawSprite(OpCodeData data)
        {
            var startX = V[data.X];
            var startY = V[data.Y];

            //Debug.WriteLine(string.Format("Drawing {0}-line sprite from {1} at {2}, {3}", data.N, I, startX, startY));

            // Write any pending clears
            for (var x = 0; x < ScreenWidth; x++)
            {
                for (var y = 0; y < ScreenHeight; y++)
                {
                    if (pendingClearBuffer[x, y])
                    {
                        pendingClearBuffer[x, y] = false;
                        buffer[x, y]             = false;
                    }
                }
            }

            V[0xF] = 0;
            for (var i = 0; i < data.N; i++)
            {
                var spriteLine = RAM[I + i];                 // A line of the sprite to render

                for (var bit = 0; bit < 8; bit++)
                {
                    var x = (startX + bit) % ScreenWidth;
                    var y = (startY + i) % ScreenHeight;

                    var spriteBit = ((spriteLine >> (7 - bit)) & 1);
                    var oldBit    = buffer[x, y] ? 1 : 0;

                    // New bit is XOR of existing and new.
                    var newBit = oldBit ^ spriteBit;

                    if (newBit != 0)
                    {
                        buffer[x, y] = true;
                    }
                    else                     // Otherwise write a pending clear
                    {
                        pendingClearBuffer[x, y] = true;
                    }

                    // If we wiped out a pixel, set flag for collission.
                    if (oldBit != 0 && newBit == 0)
                    {
                        V[0xF] = 1;
                    }
                }
            }
        }
예제 #9
0
 /// <summary>
 /// Waits for a key to be pressed by looping at the current instruction.
 /// </summary>
 void WaitForKey(OpCodeData data)
 {
     // If we have a key pressed, store it and more on.
     if (pressedKeys.Count != 0)
     {
         V[data.X] = pressedKeys.First();
     }
     else
     {
         // Otherwise, wind the PC back so we will keep executing this instruction.
         PC -= 2;
     }
 }
예제 #10
0
        // http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#3.1

        /// <summary>
        /// Handles 0x0... which either clears the screen or returns from a subroutine.
        /// </summary>
        void ClearOrReturn(OpCodeData data)
        {
            if (data.NN == 0xE0)
            {
                for (var x = 0; x < ScreenWidth; x++)
                {
                    for (var y = 0; y < ScreenHeight; y++)
                    {
                        buffer[x, y] = false;
                    }
                }
            }
            else if (data.NN == 0xEE)
            {
                PC = Pop();
            }
        }
예제 #11
0
        /// <summary>
        /// Sets V[x] to V[y].
        /// </summary>
        void Arithmetic(OpCodeData data)
        {
            switch (data.N)
            {
            case 0x0:
                V[data.X] = V[data.Y];
                break;

            case 0x1:
                V[data.X] |= V[data.Y];
                break;

            case 0x2:
                V[data.X] &= V[data.Y];
                break;

            case 0x3:
                V[data.X] ^= V[data.Y];
                break;

            case 0x4:
                V[0xF]     = (byte)(V[data.X] + V[data.Y] > 0xFF ? 1 : 0);                     // Set flag if we overflowed.
                V[data.X] += V[data.Y];
                break;

            case 0x5:
                V[0xF]     = (byte)(V[data.X] > V[data.Y] ? 1 : 0);                     // Set flag if we underflowed.
                V[data.X] -= V[data.Y];
                break;

            case 0x6:
                V[0xF]     = (byte)((V[data.X] & 0x1) != 0 ? 1 : 0); // Set flag if we shifted a 1 off the end.
                V[data.X] /= 2;                                      // Shift right.
                break;

            case 0x7:                                               // Note: This is Y-X, 5 was X-Y.
                V[0xF]     = (byte)(V[data.Y] > V[data.X] ? 1 : 0); // Set flag if we underflowed.
                V[data.Y] -= V[data.X];
                break;

            case 0xE:
                V[0xF]     = (byte)((V[data.X] & 0xF) != 0 ? 1 : 0); // Set flag if we shifted a 1 off the end.
                V[data.X] *= 2;                                      // Shift left.
                break;
            }
        }
예제 #12
0
        public void Tick()
        {
            // Read the two bytes of OpCode (big endian).
            var opCode = (ushort)(RAM[PC++] << 8 | RAM[PC++]);

            //Debug.WriteLine((PC - 2).ToString("X4") + ": " + opCode.ToString("X4"));

            // Split data into the possible formats the instruction might need.
            // https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
            var op = new OpCodeData()
            {
                OpCode = opCode,
                NNN    = (ushort)(opCode & 0x0FFF),
                NN     = (byte)(opCode & 0x00FF),
                N      = (byte)(opCode & 0x000F),
                X      = (byte)((opCode & 0x0F00) >> 8),
                Y      = (byte)((opCode & 0x00F0) >> 4),
            };

            // Loop up the OpCode using the first nibble and execute.
            opCodes[(byte)(opCode >> 12)](op);
        }
예제 #13
0
 /// <summary>
 /// Jumps to location nnn (not a subroutine, so old PC is not pushed to the stack).
 /// </summary>
 void Jump(OpCodeData data)
 {
     PC = data.NNN;
 }
예제 #14
0
 /// <summary>
 /// Sets the I register.
 /// </summary>
 void SetI(OpCodeData data)
 {
     I = data.NNN;
 }
예제 #15
0
 /// <summary>
 /// Sets I to the correct location of the font sprite V[x].
 /// Each font sprite is 5 bytes long.
 /// </summary>
 void SetIForChar(OpCodeData data)
 {
     //Debug.WriteLine(string.Format("Setting I to {0} to render a {1}", V[data.X] * 5, V[data.X].ToString("X")));
     I = (ushort)(V[data.X] * 5);             // 0 is at 0x0, 1 is at 0x5, ...
 }
예제 #16
0
 /// <summary>
 /// Takes the decimal representation of V[x] and puts each character into memory locations
 /// starting at I (with a maximum of 3).
 /// </summary>
 void BinaryCodedDecimal(OpCodeData data)
 {
     RAM[I + 0] = (byte)((V[data.X] / 100) % 10);
     RAM[I + 1] = (byte)((V[data.X] / 10) % 10);
     RAM[I + 2] = (byte)(V[data.X] % 10);
 }
예제 #17
0
 /// <summary>
 /// Play sound for V[x] 60ths of a second.
 /// </summary>
 void SetSound(OpCodeData data)
 {
     beep((int)(V[data.X] * (1000f / 60)));
 }
예제 #18
0
 /// <summary>
 /// Adds V[x] to register I.
 /// </summary>
 void AddXToI(OpCodeData data)
 {
     I += V[data.X];
 }
예제 #19
0
 /// <summary>
 /// Sets V[x] to equal the Delay register.
 /// </summary>
 void SetXToDelay(OpCodeData data)
 {
     V[data.X] = Delay;
 }
예제 #20
0
 /// <summary>
 /// Sets the delay register to V[x].
 /// </summary>
 void SetDelay(OpCodeData data)
 {
     Delay = V[data.X];
 }
예제 #21
0
 /// <summary>
 /// Jumps to location nnn + v[0] (not a subroutine, so old PC is not pushed to the stack).
 /// </summary>
 void JumpWithOffset(OpCodeData data)
 {
     PC = (ushort)(data.NNN + V[0]);
 }
예제 #22
0
 /// <summary>
 /// Jumps to subroutine nnn (unlike Jump, this pushes the previous PC to the stack to allow return).
 /// </summary>
 void CallSubroutine(OpCodeData data)
 {
     Push(PC);
     PC = data.NNN;
 }
예제 #23
0
 /// <summary>
 /// Sets V[x] == nn.
 /// </summary>
 void SetX(OpCodeData data)
 {
     V[data.X] = data.NN;
 }
예제 #24
0
 /// <summary>
 /// ANDs a random number with nn and stores in V[x].
 /// </summary>
 void Rnd(OpCodeData data)
 {
     V[data.X] = (byte)(rnd.Next(0, 256) & data.NN);
 }
예제 #25
0
 /// <summary>
 /// Adds nn to V[x].
 /// </summary>
 void AddX(OpCodeData data)
 {
     V[data.X] += data.NN;             // TODO: Do we need to handle overflow?
 }