public void Start() { Console.CursorVisible = false; while (true) { // fetch opcode var fetchedOpcode = (ushort)(memory[PC++] << 8 | memory[PC++]); var opcode = new OpcodeData { FullOpcode = fetchedOpcode, NNN = (ushort)(fetchedOpcode & 0x0FFF), X = (byte)((fetchedOpcode & 0x0F00) >> 8), Y = (byte)((fetchedOpcode & 0x00F0) >> 4), NN = (byte)(fetchedOpcode & 0x00FF), N = (byte)(fetchedOpcode & 0x000F) }; // Console.WriteLine($"{opcode.FullOpcode:X4}"); _decodeAndExecute(opcode); if (Console.KeyAvailable) { ConsoleKeyInfo keyInfo = Console.ReadKey(true); if (mappedKeys.TryGetValue(keyInfo.Key, out byte value)) { for (int i = 0; i < keys.Length; ++i) { keys[i] = 0; } keys[value] = 0xF; } } else { for (int i = 0; i < keys.Length; ++i) { keys[i] = 0; } } if (drawFlag) { _drawGraphics(); } if (delayTimer > 0) { --delayTimer; } if (soundTimer > 0) { if (soundTimer == 1) { Console.Beep(); } --soundTimer; } } }
private void _decodeAndExecute(OpcodeData opcode) { // evaluate first nibble (four bits) switch (opcode.FullOpcode & 0xF000) { case 0x0: // further evaluate second byte to determine instruction _decode0(opcode.NN); break; case 0x1000: // 1NNN - jump to address NNN PC = opcode.NNN; break; case 0x2000: // 2NNN - call subroutine at address NNN stack[SP++] = PC; PC = opcode.NNN; break; case 0x3000: // 3XNN - skip next instruction if VX equals NN if (V[opcode.X] == opcode.NN) { PC += 2; } break; case 0x4000: // 4XNN - skip next instruction if VX does not equal NN if (V[opcode.X] != opcode.NN) { PC += 2; } break; case 0x5000: // 5XY0 - skip next instruction if VX equals VY if (V[opcode.X] == V[opcode.Y]) { PC += 2; } break; case 0x6000: // 6XNN - set VX to NN V[opcode.X] = opcode.NN; break; case 0x7000: // 7XNN - add NN to VX V[opcode.X] += opcode.NN; break; case 0x8000: // further evaluate last nibble to determine instruction _decode8(opcode.N, opcode.X, opcode.Y); break; case 0x9000: // 0x9XY0 - skip next instruction if VX != VY if (V[opcode.X] != V[opcode.Y]) { PC += 2; } break; case 0xA000: // ANNN - set index register to address NNN I = opcode.NNN; break; case 0xB000: // BNNN - jump to address NNN + V0 PC = (ushort)(opcode.NNN + V[0]); break; case 0xC000: // CXNN - set VX to result of NN AND (random number from 0 to 255) V[opcode.X] = (byte)(opcode.NN & new Random().Next(256)); break; case 0xD000: // DXYN - draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and height of N pixels; // each row of 8 pixels is read as bit-coded starting from memory location I; // VF is set to 1 if any screen pixels are flipped from set to unset when sprite is drawn, 0 if not byte x = V[opcode.X]; byte y = V[opcode.Y]; V[0xF] = 0; for (byte i = 0; i < opcode.N; ++i) { byte row = memory[I + i]; byte mask = 0x80; for (byte j = 0; j < 8; ++j) { bool bit = Convert.ToBoolean(row & mask); mask >>= 1; if (bit) { if (graphics[y + i, x + j]) { V[0xF] = 1; } graphics[y + i, x + j] ^= true; } } } drawFlag = true; break; case 0xE000: // further evaluate last byte to determine instruction _decodeE(opcode.NN, opcode.X); break; case 0xF000: // further evaluate last byte to determine instruction _decodeF(opcode.NN, opcode.X, opcode.Y); break; default: throw new Exception("Invalid instruction!"); } }