示例#1
0
        public void EmulateCycle()
        {
            if (!_keyPressHault)
            {
                bool skipNextInstruction = false;
                bool incrementCounter    = true;
                bool drawFlag            = false;

                // Fetch
                Opcode = (ushort)(RAM[PC] << 8 | RAM[PC + 1]);
                byte   F   = 0xF;
                byte   X   = (byte)((Opcode & 0x0F00) >> 8);
                byte   Y   = (byte)((Opcode & 0x00F0) >> 4);
                byte   N   = (byte)(Opcode & 0x000F);
                byte   NN  = (byte)(Opcode & 0x00FF);
                ushort NNN = (ushort)(Opcode & 0x0FFF);

                // Decode & Excute
                switch ((Opcode) & 0xF000)
                {
                case 0x0000:

                    switch ((Opcode) & 0xFFF)
                    {
                    case 0x0E0:

                        // Clears the screen.
                        for (int i = 0; i < (64 * 32); i++)
                        {
                            GFX[i] = 0;
                        }

                        drawFlag = true;

                        break;

                    case 0x0EE:

                        // Returns from a subroutine.
                        StackPointer--;
                        PC = Stack[StackPointer];

                        break;

                    default:

                        _logger?.Log("Invalid opcode = " + Opcode.ToString());

                        break;
                    }

                    break;

                case 0x1000:

                    // Jumps to address NNN.
                    PC = NNN;
                    incrementCounter = false;

                    break;

                case 0x2000:

                    // Calls subroutine at NNN.
                    Stack[StackPointer] = PC;
                    ++StackPointer;
                    PC = NNN;
                    incrementCounter = false;

                    break;

                case 0x3000:

                    // Skips the next instruction if VX equals NN. (Usually the next instruction is a jump to skip a code block)
                    if (V[X] == NN)
                    {
                        skipNextInstruction = true;
                    }

                    break;

                case 0x4000:

                    // Skips the next instruction if VX doesn't equal NN. (Usually the next instruction is a jump to skip a code block)
                    if (V[X] != NN)
                    {
                        skipNextInstruction = true;
                    }

                    break;

                case 0x5000:

                    // Skips the next instruction if VX equals VY. (Usually the next instruction is a jump to skip a code block)
                    if (V[X] == V[Y])
                    {
                        skipNextInstruction = true;
                    }

                    break;

                case 0x6000:

                    // Sets VX to NN.
                    V[X] = NN;
                    break;

                case 0x7000:

                    // Adds NN to VX. (Carry flag is not changed)
                    V[X] += NN;
                    break;

                case 0x8000:

                    switch (Opcode & 0x00F)
                    {
                    case 0x000:

                        // Sets VX to the value of VY.
                        V[X] = V[Y];

                        break;

                    case 0x001:

                        // Sets VX to VX or VY. (Bitwise OR operation)
                        V[X] = (byte)(V[X] | V[Y]);

                        break;

                    case 0x002:

                        // Sets VX to VX and VY. (Bitwise AND operation)
                        V[X] = (byte)(V[X] & V[Y]);

                        break;

                    case 0x003:

                        // Sets VX to VX xor VY.
                        V[X] = (byte)(V[X] ^ V[Y]);

                        break;

                    case 0x004:

                        // Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
                        int sumVxVy = V[X] + V[Y];

                        if (sumVxVy > 255)
                        {
                            sumVxVy -= 256;
                            V[F]     = 0x01;
                        }
                        else
                        {
                            V[F] = 0x00;
                        }

                        V[X] = (byte)sumVxVy;

                        break;

                    case 0x005:

                        // VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
                        int subVxVy = V[X] - V[Y];

                        if (subVxVy < 0)
                        {
                            subVxVy += 256;
                            V[F]     = 0x00;
                        }
                        else
                        {
                            V[F] = 0x01;
                        }

                        V[X] = (byte)subVxVy;

                        break;

                    case 0x006:

                        // Stores the least significant bit of VX in VF and then shifts VX to the right by 1.[b]
                        V[F]   = (byte)(V[X] & 0b00000001);
                        V[X] >>= 1;

                        break;

                    case 0x007:

                        // Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
                        int subVyVx = V[Y] - V[X];

                        if (subVyVx < 0)
                        {
                            subVyVx += 256;
                            V[F]     = 0x00;
                        }
                        else
                        {
                            V[F] = 0x01;
                        }

                        V[X] = (byte)subVyVx;

                        break;

                    case 0x00E:

                        //  Stores the most significant bit of VX in VF and then shifts VX to the left by 1.[b]
                        V[F]   = (byte)(V[X] & 0b10000000);
                        V[X] <<= 1;

                        break;

                    default:

                        _logger?.Log("Invalid opcode = " + Opcode.ToString());

                        break;
                    }

                    break;

                case 0x9000:

                    // Skips the next instruction if VX doesn't equal VY. (Usually the next instruction is a jump to skip a code block)
                    if (V[X] != V[Y])
                    {
                        skipNextInstruction = true;
                    }

                    break;

                case 0xA000:

                    // Sets I to the address NNN.
                    I = NNN;

                    break;

                case 0xB000:

                    //  Jumps to the address NNN plus V0.
                    PC = (ushort)(V[0] + NNN);
                    incrementCounter = false;

                    break;

                case 0xC000:

                    // Sets VX to the result of a bitwise and operation on a random number (Typically: 0 to 255) and NN.
                    V[X] = (byte)(_randomNumber.RandomNumber() & NN);

                    break;

                case 0xD000:

                    // Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a height of N pixels.
                    // Each row of 8 pixels is read as bit-coded starting from memory location I; I value doesn’t change
                    // after the execution of this instruction. 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
                    int    height = N;
                    ushort pixel;

                    V[0xF] = 0;
                    for (int yline = 0; yline < height; yline++)
                    {
                        pixel = RAM[I + yline];
                        for (int xline = 0; xline < 8; xline++)
                        {
                            if ((pixel & (0b10000000 >> xline)) != 0)
                            {
                                int posX = V[X] + xline;
                                int posY = V[Y] + yline;

                                // Check if sprite overflows screen
                                if (posX >= 64)
                                {
                                    posX -= 64;
                                }
                                if (posY >= 32)
                                {
                                    posY -= 32;
                                }

                                // Check if pixel is already set, if so raise V[F] flag
                                if (GFX[(posX + (posY * 64))] == 1)
                                {
                                    V[0xF] = 1;
                                }

                                GFX[posX + (posY * 64)] ^= 1;
                            }
                        }
                    }
                    drawFlag = true;

                    break;

                case 0xE000:

                    switch (Opcode & 0x0FF)
                    {
                    case 0x09E:

                        // Skips the next instruction if the key stored in VX is pressed. (Usually the next instruction is a jump to skip a code block)
                        if (Keys[V[X]] == 1)
                        {
                            skipNextInstruction = true;
                        }

                        break;

                    case 0x0A1:

                        // Skips the next instruction if the key stored in VX isn't pressed. (Usually the next instruction is a jump to skip a code block)
                        if (Keys[V[X]] == 0)
                        {
                            skipNextInstruction = true;
                        }

                        break;

                    default:

                        _logger?.Log("Invalid opcode = " + Opcode.ToString());

                        break;
                    }

                    break;

                case 0xF000:

                    switch (Opcode & 0x00FF)
                    {
                    case 0x007:

                        // Sets VX to the value of the delay timer.
                        V[X] = DelayTimer;

                        break;

                    case 0x00A:

                        // A key press is awaited, and then stored in VX.
                        // (Blocking Operation. All instruction halted until next key event)
                        _keyPressHault = true;
                        _keyVIndex     = X;

                        break;

                    case 0x015:

                        // Sets the delay timer to VX.
                        DelayTimer = V[X];

                        break;

                    case 0x018:

                        // Sets the sound timer to VX.
                        SoundTimer = V[X];

                        break;

                    case 0x01E:

                        // Adds VX to I. VF is not affected.
                        I += V[X];

                        break;

                    case 0x029:

                        // Sets I to the location of the sprite for the character in VX.
                        // Characters 0-F (in hexadecimal) are represented by a 4x5 font.
                        I = (ushort)(V[X] * 5);

                        break;

                    case 0x033:

                        // Stores the binary-coded decimal representation of VX, with the most significant of three
                        // digits at the address in I, the middle digit at I plus 1, and the least significant digit
                        // at I plus 2. (In other words, take the decimal representation of VX, place the hundreds digit
                        // in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2.)
                        RAM[I]     = (byte)(V[X] / 100);
                        RAM[I + 1] = (byte)((V[X] / 10) % 10);
                        RAM[I + 2] = (byte)((V[X] % 100) % 10);

                        break;

                    case 0x055:

                        // Stores V0 to VX (including VX) in memory starting at address I.
                        // The offset from I is increased by 1 for each value written, but I itself is left unmodified.[d]
                        for (byte i = 0; i <= X; i++)
                        {
                            RAM[I + i] = V[i];
                        }

                        break;

                    case 0x065:

                        // Fills V0 to VX (including VX) with values from memory starting at address I.
                        // The offset from I is increased by 1 for each value written, but I itself is left unmodified.[d]
                        for (byte i = 0; i <= X; i++)
                        {
                            V[i] = RAM[I + i];
                        }

                        break;

                    default:

                        _logger?.Log("Invalid opcode = " + Opcode.ToString("X"));

                        break;
                    }

                    break;
                }

                if (skipNextInstruction)
                {
                    PC += 4;
                }
                else if (incrementCounter)
                {
                    PC += 2;
                }

                if (drawFlag)
                {
                    _screenRenderer.DrawGFX(GFX);
                }
            }

            _60HzCounter--;
            if (_60HzCounter <= 0)
            {
                _60HzCounter = (byte)(_clockSpeed / 60f);

                // Update timers
                if (DelayTimer > 0)
                {
                    --DelayTimer;
                }

                if (SoundTimer > 0)
                {
                    if (SoundTimer == 1)
                    {
                        _beep.Beep();
                    }
                    --SoundTimer;
                }
            }
        }