Tutorial de creación de un Emulador sencillo de Chip-8 con VS 2008 y C# (Parte 7)Lenguaje: C# 2.0Para: VS 2008 con Sdl.Net 6.1 Por Dark-N: hernaldog@gmail.com http://darknromhacking.com Hilo del Foro: http://foro.romhackhispano.org/viewtopic.php?f=4&t=872 |
///Aquí debemos incluir la nueva componente con la variable SdlDotNet.Windows.SurfaceControl scDisplay que representará la parte gráfica del emulador y lo ubicamos abajo del control de Menú, es decir en el punto 0,27 y con una resolución de 640 x 320::/// Required designer variable. /// private System.ComponentModel.IContainer components = null; ... #region Windows Form Designer generated code ////// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Text = "Chip8"; } #endregion
///Se debe agregar el recurso "scDisplay.Image" al proyecto, para esto copia esta pequeña imagen/// Required designer variable. /// private System.ComponentModel.IContainer components = null; // Control gráfico dentro de la ventana que permite mostrar el juego private SdlDotNet.Windows.SurfaceControl scDisplay; ... private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Chip8)); this.scDisplay = new SdlDotNet.Windows.SurfaceControl(); ((System.ComponentModel.ISupportInitialize)(this.scDisplay)).BeginInit(); ... // seteamos el control gráfico scDisplay para el emulador this.scDisplay.AccessibleDescription = "SdlDotNet SurfaceControl"; this.scDisplay.AccessibleName = "SurfaceControl"; this.scDisplay.AccessibleRole = System.Windows.Forms.AccessibleRole.Graphic; this.scDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.scDisplay.Image = ((System.Drawing.Image)(resources.GetObject("scDisplay.Image"))); this.scDisplay.InitialImage = null; this.scDisplay.Location = new System.Drawing.Point(0, 27); this.scDisplay.Name = "scDisplay"; this.scDisplay.Size = new System.Drawing.Size(640, 320); this.scDisplay.TabIndex = 0; this.scDisplay.TabStop = false; // Agregamos el control gráfico this.Controls.Add(this.scDisplay); }
using SdlDotNet.Graphics; using SdlDotNet.Core;En la clase del formulario Chip8.cs, creamos una nueva instancia de la clase Emulador:
namespace Chip8 { public partial class Chip8 : Form { Emulador Emu; public Chip8() { InitializeComponent(); Emu = new Emulador(); } } }
// Actualiza control gráfico con lo que entrega el Surface de la clase Emulador.cs private void UpdateDisplay() { scDisplay.Blit(Emu.SurfaceOut); scDisplay.Update(); }
//private void Events_Tick(object sender, TickEventArgs e) //{ // Video.WindowCaption = "Emulador Chip-8 FPS: " + Events.Fps; // EmulaFrame(); //} ... public Emulador() { ... //Events.Tick += new EventHandlery este Evento lo manejaremos por el formulario, por lo que se debe inicializar con SdlDotNet.Core.Events.Tick += ... en la clase Chip8.cs y crear el método que llama a UpdateDisplay():(this.Events_Tick); ... }
// constructor public Chip8() { ... SdlDotNet.Core.Events.Tick += new EventHandler(Events_Tick); } ... void Events_Tick(object sender, TickEventArgs e) { Emu.EmulaFrame(); UpdateDisplay(); }
//static void Main(string[] args) //{ // Emulador emulador = new Emulador(); // emulador.Run(); //}Dejaremos que el emulador se inicie desde otra clase, es más elegante y es un punto básico de que se denomina Programación orientada a objetos. Por lo que creamos la clase Programa.cs y adentro escribimos el código que "lanza" el emulador, es decir creamos una instancia de la clase con "new Chip8()":
using System; using System.Windows.Forms; namespace Chip8 { class Programa { [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Chip8()); } } }
///En el constructor y otras partes del código cambiamos el uso de Video.Screen por nuestra nueva variable de salida surfaceOut, a la vez de incrementar la variable que guarda el largo/alto de un pixel tamanoPixel de 5 a 10:/// Salida de gráficos /// private Surface surfaceOut; public Surface SurfaceOut { get { return surfaceOut; } }
const int tamanoPixel = 10; // tamaño de pixel, sirve para zoom de todo el emulador ///Cambiamos el método ResetHardware ya que usamos el método ClearScreen():/// Constructor de la clase /// public Emulador() { try { //Video.SetVideoMode(RES_X * tamanoPixel, RES_Y * tamanoPixel); //Video.Screen.Fill(Color.Black); //Events.Fps = 60; surfaceOut = Video.CreateRgbSurface(RES_X * tamanoPixel, RES_Y * tamanoPixel);
void ResetHardware() { ... //Video.Screen.Fill(Color.Black); //Video.Screen.Update(); ClearScreen(); }Cambiamos el método ClearScreen que se encarga de dejar la pantalla en negro, cambiando Video.Screen por surfaceOut:
void ClearScreen() { surfaceOut.Fill(Color.Black); surfaceOut.Update(); ...Cambiamos el método DrawSprite que se encarga pintar los pixels, cambiando Video.Screen por surfaceOut:
void DrawSprite() { ... if (arregloPantalla[xx % 64, yy % 32] == 1) { arregloPantalla[xx % 64, yy % 32] = 0; surfaceOut.Blit(PixelNegro, new Point((xx % 64) * tamanoPixel, (yy % 32) * tamanoPixel)); V[0xF] = 1; //colision activado } else { arregloPantalla[xx % 64, yy % 32] = 1; surfaceOut.Blit(PixelBlanco, new Point((xx % 64) * tamanoPixel, (yy % 32) * tamanoPixel)); } ... surfaceOut.Update(); ... }
Emulador.cs // constructor public Emulador() { try { ... //ResetHardware(); //CargarJuego("PONG"); ... public void CargarJuego(string nombreRom) //dejamos público el método { .. } Chip8.cs // Evento clic en opción del menú "Cargar Juego" private void cargarJuegoToolStripMenuItem1_Click(object sender, EventArgs e) { Emu.ResetHardware(); Emu.CargarJuego("PONG"); SdlDotNet.Core.Events.Run(); }
// Evento gatillado al cerrar la aplicación con la "x" de la ventana private void Chip8_FormClosed(object sender, FormClosedEventArgs e) { CerrarAplicacion(); } // Método que termina los eventos y cierra la aplicación void CerrarAplicacion() { SdlDotNet.Core.Events.Close(); SdlDotNet.Core.Events.QuitApplication(); Application.Exit(); } // Evento gatillado al presionar Salir private void salirToolStripMenuItem_Click(object sender, EventArgs e) { CerrarAplicacion(); }En Emulador.cs comentamos el método que cierra la aplicación que se controla desde el formulario, lo comentamos en el constructor y en el evento de Teclas Presionadas.
public Emulador() { try { ... //Events.Quit += new EventHandlerEn el editor de Visual Studio, si presionamos F5 ya podemos ver el emulador y allí vamos a "Archivo" -> "Cargar Rom" se cargará el emulador con el juego PONG:(this.Events_Salir); ... } ... //private void Events_Salir(object sender, QuitEventArgs e) //{ // Events.QuitApplication(); //} } ... private void Tecla_Presionada(object sender, KeyboardEventArgs e) { ////Salimos del juego con tecla Escape //if (e.Key == Key.Escape) //{ // Events.QuitApplication(); //} ... }
private int TargetFPS; // constructor public Chip8() { ... this.Text = "Emulador Chip-8"; SdlDotNet.Core.Events.Tick += new EventHandlerEl Pause es muy importante en cualquier emulador. Lo primero es definir una variable de clase booleana que nos diga si el juego está pausado o no, le llamaremos "estaPausado", luego en el Event_Tick se debe validar que no ejecute los frames si está pausado, y finalmente generar el evento "click" de la opción "Pausa" con el ícono de eventos (rayo), con esto se genera el método pausaToolStripMenuItem_Click y adentro setear la variable "estaPausado" que definimos:(Events_Tick); TargetFPS = 60; SdlDotNet.Core.Events.Fps = 60; } ... void Events_Tick(object sender, TickEventArgs e) { this.Text = "Emulador Chip-8 FPS: " + SdlDotNet.Core.Events.Fps; ... }
private bool estaPausado; ... // constructor public Chip8() { estaPausado = false; } ... // Game-Loop void Events_Tick(object sender, TickEventArgs e) { this.Text = "Emulador Chip-8 FPS: " + SdlDotNet.Core.Events.Fps; if (estaPausado == false) { Emu.EmulaFrame(); UpdateDisplay(); } } ... // Evento gatillado al presionar Pausa private void pausaToolStripMenuItem_Click(object sender, EventArgs e) { if (estaPausado == false) estaPausado = true; else estaPausado = false; }El Reset también es muy importante, así que imagino que saben que vamos a hacer: generamos el evento "click" de la opción "Reset" con el ícono de eventos (rayo) y adentro llamamos al método Reset de la clase Emulador.cs. No olvidar dejar público este último método.
Emulador.cs public void ResetHardware() //lo dejamos público { ... } Chip8.cs // Evento gatillado al presionar Reset private void resetToolStripMenuItem_Click(object sender, EventArgs e) { Reset(); } // Resetamos el hardware (registros, memoria) y cargamos de nuevo el juego void Reset() { Emu.ResetHardware(); Emu.CargarJuego(nombreROM); // en el siguiente paso definimos nombreROM }Para hacer la la ventana de diálogo de carga de ROM se debe primero agregar el control System.Windows.Forms.OpenFileDialog al formulario, para esto lo buscamos en el ToolBox (Control + Alt + X) y lo arrastramos al panel inferior de la ventana de diseño.
private string nombreROM = string.Empty; ... // Evento clic en opción del menú "Cargar Juego" private void cargarJuegoToolStripMenuItem1_Click(object sender, EventArgs e) { openFileDialog1.FileName = string.Empty; if (openFileDialog1.ShowDialog() == DialogResult.OK) { nombreROM = openFileDialog1.FileName; Reset(); SdlDotNet.Core.Events.Run(); } }Con esto podremos cargar cualquier juego y no sólo la ROM de PONG:
//using SdlDotNet.Core; //Eventos //using SdlDotNet.Input; //para manejo de teclado ... public bool[] teclasPresionadas = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }; ... //constructor public Emulador() { ... //Events.KeyboardDown += new EventHandlerLo segundo es modificar la clase Chip8.cs ya que ahora usamos el delegado KeyEventHandler del Framework .Net ya que el teclado lo manejamos desde el formulario y no la clase KeyboardEventArgs de SDL.Net como lo hacíamos antes:(this.Tecla_Presionada); //Events.KeyboardUp += new EventHandler (this.Tecla_Liberada); ... } ... //private void Tecla_Presionada(object sender, KeyboardEventArgs e) //{ // ////Salimos del juego con tecla Escape // //if (e.Key == Key.Escape) // //{ // // Events.QuitApplication(); // //} // if (e.Key == Key.One) { teclasPresionadas[TECLA_1] = true; } // if (e.Key == Key.Two) { teclasPresionadas[TECLA_2] = true; } // if (e.Key == Key.Three) { teclasPresionadas[TECLA_3] = true; } // if (e.Key == Key.Four) { teclasPresionadas[TECLA_4] = true; } // if (e.Key == Key.Q) { teclasPresionadas[TECLA_Q] = true; } // if (e.Key == Key.W) { teclasPresionadas[TECLA_W] = true; } // if (e.Key == Key.E) { teclasPresionadas[TECLA_E] = true; } // if (e.Key == Key.R) { teclasPresionadas[TECLA_R] = true; } // if (e.Key == Key.A) { teclasPresionadas[TECLA_A] = true; } // if (e.Key == Key.S) { teclasPresionadas[TECLA_S] = true; } // if (e.Key == Key.D) { teclasPresionadas[TECLA_D] = true; } // if (e.Key == Key.F) { teclasPresionadas[TECLA_F] = true; } // if (e.Key == Key.Z) { teclasPresionadas[TECLA_Z] = true; } // if (e.Key == Key.X) { teclasPresionadas[TECLA_X] = true; } // if (e.Key == Key.C) { teclasPresionadas[TECLA_C] = true; } // if (e.Key == Key.V) { teclasPresionadas[TECLA_V] = true; } //} //private void Tecla_Liberada(object sender, KeyboardEventArgs e) //{ // if (e.Key == Key.One) { teclasPresionadas[TECLA_1] = false; } // if (e.Key == Key.Two) { teclasPresionadas[TECLA_2] = false; } // if (e.Key == Key.Three) { teclasPresionadas[TECLA_3] = false; } // if (e.Key == Key.Four) { teclasPresionadas[TECLA_4] = false; } // if (e.Key == Key.Q) { teclasPresionadas[TECLA_Q] = false; } // if (e.Key == Key.W) { teclasPresionadas[TECLA_W] = false; } // if (e.Key == Key.E) { teclasPresionadas[TECLA_E] = false; } // if (e.Key == Key.R) { teclasPresionadas[TECLA_R] = false; } // if (e.Key == Key.A) { teclasPresionadas[TECLA_A] = false; } // if (e.Key == Key.S) { teclasPresionadas[TECLA_S] = false; } // if (e.Key == Key.D) { teclasPresionadas[TECLA_D] = false; } // if (e.Key == Key.F) { teclasPresionadas[TECLA_F] = false; } // if (e.Key == Key.Z) { teclasPresionadas[TECLA_Z] = false; } // if (e.Key == Key.X) { teclasPresionadas[TECLA_X] = false; } // if (e.Key == Key.C) { teclasPresionadas[TECLA_C] = false; } // if (e.Key == Key.V) { teclasPresionadas[TECLA_V] = 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;
// variables para cada tecla 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; // constructor public Chip8() { ... this.KeyDown += new KeyEventHandler(this.Tecla_Presionada); this.KeyUp += new KeyEventHandler(this.Tecla_Liberada); ... } private void Tecla_Presionada(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.NumPad1) { Emu.teclasPresionadas[TECLA_1] = true; } if (e.KeyCode == Keys.NumPad2) { Emu.teclasPresionadas[TECLA_2] = true; } if (e.KeyCode == Keys.NumPad3) { Emu.teclasPresionadas[TECLA_3] = true; } if (e.KeyCode == Keys.NumPad4) { Emu.teclasPresionadas[TECLA_4] = true; } if (e.KeyCode == Keys.Q) { Emu.teclasPresionadas[TECLA_Q] = true; } if (e.KeyCode == Keys.W) { Emu.teclasPresionadas[TECLA_W] = true; } if (e.KeyCode == Keys.E) { Emu.teclasPresionadas[TECLA_E] = true; } if (e.KeyCode == Keys.R) { Emu.teclasPresionadas[TECLA_R] = true; } if (e.KeyCode == Keys.A) { Emu.teclasPresionadas[TECLA_A] = true; } if (e.KeyCode == Keys.S) { Emu.teclasPresionadas[TECLA_S] = true; } if (e.KeyCode == Keys.D) { Emu.teclasPresionadas[TECLA_D] = true; } if (e.KeyCode == Keys.F) { Emu.teclasPresionadas[TECLA_F] = true; } if (e.KeyCode == Keys.Z) { Emu.teclasPresionadas[TECLA_Z] = true; } if (e.KeyCode == Keys.X) { Emu.teclasPresionadas[TECLA_X] = true; } if (e.KeyCode == Keys.C) { Emu.teclasPresionadas[TECLA_C] = true; } if (e.KeyCode == Keys.V) { Emu.teclasPresionadas[TECLA_V] = true; } } private void Tecla_Liberada(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.NumPad1) { Emu.teclasPresionadas[TECLA_1] = false; } if (e.KeyCode == Keys.NumPad2) { Emu.teclasPresionadas[TECLA_2] = false; } if (e.KeyCode == Keys.NumPad3) { Emu.teclasPresionadas[TECLA_3] = false; } if (e.KeyCode == Keys.NumPad4) { Emu.teclasPresionadas[TECLA_4] = false; } if (e.KeyCode == Keys.Q) { Emu.teclasPresionadas[TECLA_Q] = false; } if (e.KeyCode == Keys.W) { Emu.teclasPresionadas[TECLA_W] = false; } if (e.KeyCode == Keys.E) { Emu.teclasPresionadas[TECLA_E] = false; } if (e.KeyCode == Keys.R) { Emu.teclasPresionadas[TECLA_R] = false; } if (e.KeyCode == Keys.A) { Emu.teclasPresionadas[TECLA_A] = false; } if (e.KeyCode == Keys.S) { Emu.teclasPresionadas[TECLA_S] = false; } if (e.KeyCode == Keys.D) { Emu.teclasPresionadas[TECLA_D] = false; } if (e.KeyCode == Keys.F) { Emu.teclasPresionadas[TECLA_F] = false; } if (e.KeyCode == Keys.Z) { Emu.teclasPresionadas[TECLA_Z] = false; } if (e.KeyCode == Keys.X) { Emu.teclasPresionadas[TECLA_X] = false; } if (e.KeyCode == Keys.C) { Emu.teclasPresionadas[TECLA_C] = false; } if (e.KeyCode == Keys.V) { Emu.teclasPresionadas[TECLA_V] = false; } }
using System; using System.Windows.Forms; namespace Chip8 { class Programa { [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Chip8()); } } }
using System; using System.IO; //Librería de .Net para manejar archivos using System.Runtime.InteropServices; //para el "beep" de la Bios using System.Drawing; //Librería de .Net para manejar Colores using System.Windows.Forms; //Para enviar mensajes en ventanas de dialogos using SdlDotNet.Graphics; //para Surfaces using SdlDotNet.Graphics.Sprites; //para Sprites y textos en pantalla namespace Chip8 { class Emulador { #region variables emulador //variables principales const int DIR_INICIO = 0x200; const int TAMANO_MEM = 0xFFF; const int TAMANO_PILA = 16; const int CANT_REGISTROS = 16; //arreglo que representa la memoria int[] memoria = new int[TAMANO_MEM]; //Arreglo que representa los 16 Registros (V) int[] V = new int[CANT_REGISTROS]; //arreglo que representa la Pila int[] pila = new int[TAMANO_PILA]; //variables que representan registros varios int instruccion; //representa una instruccion del Chip-8. Tiene largo 2 byte int PC; int I; int SP; int KK; //resolucion de pantalla 64x32 (mas alta que larga) const int RES_X = 64; const int RES_Y = 32; int[,] arregloPantalla = new int[RES_X, RES_Y]; //variables para manejar los opcodes int opcode1 = 0; int opcode2 = 0; //X int opcode3 = 0; //Y int opcode4 = 0; int NNN = 0; //variables que representan los 2 timers: Delay Timer y Sound Timer int delayTimer; int soundTimer; //variable para manejo del sonido bool ejecutaSonido = true; //variables para el manejo de fuentes (80 bytes, ya que hay 5 bytes x caracter //y son 16 caracteres o letras (5x16=80). Cada font es de 4x5 bits. int[] arregloFuentes = { 0xF0, 0x90, 0x90, 0x90, 0xF0, // valores para 0 0x60, 0xE0, 0x60, 0x60, 0xF0, // valores para 1 0x60, 0x90, 0x20, 0x40, 0xF0, // valores para 2 0xF0, 0x10, 0xF0, 0x10, 0xF0, // valores para 3 0x90, 0x90, 0xF0, 0x10, 0x10, // valores para 4 0xF0, 0x80, 0x60, 0x10, 0xE0, // valores para 5 0xF0, 0x80, 0xF0, 0x90, 0xF0, // valores para 6 0xF0, 0x10, 0x10, 0x10, 0x10, // valores para 7 0xF0, 0x90, 0xF0, 0x90, 0xF0, // valores para 8 0xF0, 0x90, 0xF0, 0x10, 0x10, // valores para 9 0x60, 0x90, 0xF0, 0x90, 0x90, // valores para A 0xE0, 0x90, 0xE0, 0x90, 0xE0, // valores para B 0x70, 0x80, 0x80, 0x80, 0x70, // valores para C 0xE0, 0x90, 0x90, 0x90, 0xE0, // valores para D 0xF0, 0x80, 0xF0, 0x80, 0xF0, // valores para E 0xF0, 0x90, 0xF0, 0x80, 0x80 // valores para F }; public bool[] teclasPresionadas = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }; // mapeamos las 16 teclas de Chip8 private byte[] MapeoTeclas = { 0x01,0x02,0x03,0x0C, 0x04,0x05,0x06,0x0D, 0x07,0x08,0x09,0x0E, 0x0A,0x00,0x0B,0x0F }; [DllImport("Kernel32.dll")] //para beep public static extern bool Beep(UInt32 frequency, UInt32 duration); //Variable de tipo Random (para generar números aleatoios) utilizada por ciertas instrucciones Random rnd = new Random(); private Surface PixelNegro; private Surface PixelBlanco; const int tamanoPixel = 10; // tamaño de pixel, sirve para zoom de todo el emulador public const int operPorSegundo = 600; public const int operPorFrame = operPorSegundo / 60; private int TimeUntilTimerUpdate; //para la simulación de timers #endregion ///Chip8.cs/// Salida de gráficos /// private Surface surfaceOut; public Surface SurfaceOut { get { return surfaceOut; } } ////// Constructor de la clase /// public Emulador() { try { surfaceOut = Video.CreateRgbSurface(RES_X * tamanoPixel, RES_Y * tamanoPixel); PixelNegro = Video.CreateRgbSurface(tamanoPixel, tamanoPixel); PixelBlanco = Video.CreateRgbSurface(tamanoPixel, tamanoPixel); PixelNegro.Fill(Color.Black); PixelBlanco.Fill(Color.White); } catch (Exception ex) { MessageBox.Show("Error General: " + ex.Message + "-" + ex.StackTrace); } } public void ResetHardware() { // Reseteamos los Timers delayTimer = 0x0; soundTimer = 0x0; // Reseteamos variables instruccion = 0x0; PC = DIR_INICIO; SP = 0x0; I = 0x0; // Limpiamos la memoria for (int i = 0; i < TAMANO_MEM; i++) memoria[i] = 0x0; // Limpiamos registros for (int i = 0; i < CANT_REGISTROS; i++) V[i] = 0x0; // Limpiamos el stack for (int i = 0; i < TAMANO_PILA; i++) pila[i] = 0x0; // Cargamos las fuentes en la memoria por ej, los marcadores del juego PONG esos "[0-0]", "[0-2]" for (int i = 0; i < 80; i++) memoria[i] = arregloFuentes[i]; ClearScreen(); } // Carga la ROM (archivo) a la memoria (arreglo) public void CargarJuego(string nombreRom) { FileStream rom; try { rom = new FileStream(@nombreRom, FileMode.Open); if (rom.Length == 0) { throw new Exception("Error en la Carga de la Rom: ROM dañada o vacía"); } // Comenzamos a cargar la rom a la memoria a partir de la dir 0x200 for (int i = 0; i < rom.Length; i++) memoria[DIR_INICIO + i] = (byte)rom.ReadByte(); rom.Close(); } catch (Exception ex) { throw new Exception("Error en la Carga de la Rom: " + ex.Message); } } // Emula la ejecución de un Frame public void EmulaFrame() //representa un frame { if (soundTimer > 0 && ejecutaSonido == true) { Console.Beep(350, 50); ejecutaSonido = false; } //por cada Tick o Frame, se ejecutan 600/60=10 instrucciones for (int i = 0; i < operPorFrame; i++) { EmulaOpcodes(); } } // Emula la ejecución de un Opcode void EmulaOpcodes() { if (TimeUntilTimerUpdate == 0) { if (delayTimer > 0) delayTimer--; if (soundTimer > 0) soundTimer--; TimeUntilTimerUpdate = operPorFrame; } else { TimeUntilTimerUpdate--; } EjecutaOpcodes(); } // Lee las instrucciones de la memoria y ejecuta los Opcodes void EjecutaOpcodes() { #region lectura de instrucciones // leemos cada una de las instrucciones desde la memoria. // cada instruccion es de 2 bytes instruccion = memoria[PC] << 8 | memoria[PC + 1]; // dejamos incrementado el Program Counter en 2, lista para leer // la siguiente instruccion en el siguiente ciclo. PC += 2; #endregion #region extracción de opcodes //obtengo el valor del registro KK, de largo 1 byte, el más chico de la instrucción KK = (instruccion & 0x00FF); // cada opcode es de 4 bit opcode1 = (instruccion & 0xF000) >> 12; //los 4 bits mayores de la instrucción opcode2 = (instruccion & 0x0F00) >> 8; //X opcode3 = (instruccion & 0x00F0) >> 4; //Y opcode4 = (instruccion & 0x000F) >> 0; //Opcode N = los 4 bits menores de la instrucción //obtengo el valor del opcode NNN NNN = (instruccion & 0x0FFF); #endregion #region ejecución de instrucciones // Ejecutamos las instrucciones a travez de los opcodes switch (opcode1) { // opcodes del tipo 0xxx case (0x0): { switch (instruccion) { // opcode 00E0: Clear Screen. case (0x00E0): { ClearScreen(); break; } // opcode 00EE: Return From Subroutine. case (0x00EE): { ReturnFromSub(); break; } } break; } // opcodes del tipo 1xxx case (0x1): { // opcode 1NNN: Jump To Address NNN. JumpToAddr(); break; } // opcodes del tipo 2xxx case (0x2): { // opcode 2NNN: Call Subroutine At Address NNN. CallSub(); break; } // opcodes del tipo 3xxx case (0x3): { // opcode 4XKK: Skip Next Instruction If VX == KK SkipIfEql(); break; } // opcodes del tipo 4xxx case (0x4): { // opcode 4XKK: Skip Next Instruction If VX != KK SkipIfNotEql(); break; } // opcodes del tipo 5xxx case (0x5): { // opcode 5XY0: Skip Next Instruction If VX == VY SkipIfRegEql(); break; } // opcodes del tipo 6xxx case (0x6): { // opcode 6XKK: Assign Number KK To Register X. AssignNumToReg(); break; } // opcodes del tipo 7xxx case (0x7): { // opcode 7XKK: Add Number KK To Register X. AddNumToReg(); break; } // opcodes del tipo 8xxx case (0x8): { //Tenemos varios tipos 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; } // opcodes del tipo 9xxx case (0x9): { // opcode 9XY0: Skip Next Instruction If VX != VY SkipIfRegNotEql(); break; } // opcodes del tipo AXXX case (0xA): { // opcode ANNN: Set Index Register To Address NNN. AssignIndexAddr(); break; } // opcodes del tipo BXXX case (0xB): { // opcode BNNN: Jump To NNN + V0. JumpWithOffset(); break; } // opcodes del tipo CXXX case (0xC): { // opcode CXKK: Assign Bitwise AND Of Random Number & KK To Register X. RandomANDnum(); break; } // opcodes del tipo DXXX case (0xD): { // opcode DXYN: Draw Sprite To The Screen. DrawSprite(); break; } // opcodes del tipo EXXX case (0xE): { // Tenemos 2 tipos según EXKK switch (KK) { // opcode EX9E: Skip Next Instruction If Key In VX Is Pressed. case (0x9E): { SkipIfKeyDown(); break; } // opcode EXA1: Skip Next Instruction If Key In VX Is NOT Pressed. case (0xA1): { SkipIfKeyUp(); break; } } break; } // opcodes del tipo FXXX case (0xF): { // tenemos varios tipos de Opcodes switch (KK) { // 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; } } #endregion } #region implementación de métodos para cada instrucción void ClearScreen() { //Video.Screen.Fill(Color.Black); //Video.Screen.Update(); surfaceOut.Fill(Color.Black); surfaceOut.Update(); for (int p = 0; p < RES_X; p++) for (int q = 0; q < RES_Y; q++) arregloPantalla[p, q] = 0; } void ReturnFromSub() { // Se diminuye el SP en 1 SP--; // Apuntamos el PC (que apunta a la sgte. instrucción a ejecutar) a la // posición salvada en la Pila PC = pila[SP]; } void JumpToAddr() { // salta a la instrucción dada. No se salta directamente, sino que se le indica // al PC que vaya a dicha direccion luego de salir del actual ciclo. PC = NNN; } void CallSub() { // Salva la posicion actual del PC en la Pila, para volver a penas se ejecute la subrutina pila[SP] = PC; SP++; // Saltamos a la subrutina indicada en NNN PC = NNN; } void SkipIfEql() { // Recordar que Opcode2=X if (V[opcode2] == KK) { // Salta a la siguiente instruccion PC += 2; } } void SkipIfNotEql() { if (V[opcode2] != KK) { // Salta a la siguiente instruccion PC += 2; } } void SkipIfRegEql() { if (V[opcode2] == V[opcode3]) { // Salta a la siguiente instruccion PC += 2; } } void AssignNumToReg() { V[opcode2] = KK; } /** entrega los 8 bits menores (char) de un numero de 16 bits (int) @param number el numero de 16 bit @return el numero de 8 bits **/ private char getLower(int number) { return (char)(number&0xFF); } void AddNumToReg() { V[opcode2] += KK; } void AssignRegToReg() { //VX = VY V[opcode2] = V[opcode3]; } void RegisterOR() { // OR binario es |, entonces hacemos VX = VX | VY o mas elegante VX |= VY V[opcode2] |= V[opcode3]; } void RegisterAND() { // OR binario es &, entonces hacemos VX = VX & VY o mas elegante VX &= VY V[opcode2] &= V[opcode3]; } void RegisterXOR() { // XOR es ^, entonces hacemos VX = VX ^ VY o mas elegante VX ^= VY V[opcode2] ^= V[opcode3]; } void AddRegToReg() { //Con >> extraemos los 8 bits mayores de la suma, si el resultado supera //los 8 bits el >> 8 dara 1 si no 0 V[0xF] = (V[opcode2] + V[opcode3]) >> 8; //VX = VX + VY V[opcode2] += V[opcode3]; } void SubRegFromReg() { //seteamos F en 1 si VX > VY if (V[opcode2] >= V[opcode3]) V[0xF] = 0x1; else V[0xF] = 0x0; //VX = VX - VY V[opcode2] -= V[opcode3]; } void ShiftRegRight() { //VF = VX AND 1 (VF valdrá 1 o 0). Para este case es más optimo V[0xF] = V[opcode2] & 0x1; //Manera elegante de escribir un shift a la derecha para dividir por 2: V[opcode2] = V[opcode2] >> 1; V[opcode2] >>= 1; } void ReverseSubRegs() { if (V[opcode2] <= V[opcode3]) { V[0xF] = 0x1; } else { V[0xF] = 0x0; } V[opcode2] = V[opcode3] - V[opcode2]; } void ShiftRegLeft() { //VF = VX AND 10 hex V[0xF] = V[opcode2] & 0x10; //Manera elegante de escribir un shift a la izquierda para multiplicar por 2: V[opcode2] = V[opcode2] << 1; V[opcode2] <<= 1; } void SkipIfRegNotEql() { if (V[opcode2] != V[opcode3]) { //Aumentamos el PC en 2 para saltar a la siguiente instrucción PC += 2; } } void AssignIndexAddr() { // se setea el Registro de Índice (I) a la dirección NNN. I = NNN; } void JumpWithOffset() { PC = NNN + V[0x0]; } void RandomANDnum() { //usamos el variable rnd seteada en la clase. Con el método Next se le puede dar el mínimo (0) y máximo (255) int numeroRnd = rnd.Next(0,255); V[opcode2] = numeroRnd & 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() { // Reseteamos el registro que detecta colisiones V[0xF] = 0x0; if ((instruccion & 0x000F) == 0) //opcode & 0x000F =opcode4 { // Dibujamos un Sprite de SCHIP8 de tamaño 16x16 // No implementado aún } else { // Bibujamos un Sprite de CHIP8 de tamaño 8xN for (int spriY = 0; spriY < opcode4; spriY++) { for (int spriX = 0; spriX < 8; spriX++) { int x = (memoria[I + spriY] & (0x80 >> spriX)); if (x != 0) { // checkeamos por alguna colision int xx = (V[opcode2] + spriX); int yy = (V[opcode3] + spriY); if (arregloPantalla[xx % 64, yy % 32] == 1) { arregloPantalla[xx % 64, yy % 32] = 0; surfaceOut.Blit(PixelNegro, new Point((xx % 64) * tamanoPixel, (yy % 32) * tamanoPixel)); V[0xF] = 1; //colision activado } else { arregloPantalla[xx % 64, yy % 32] = 1; surfaceOut.Blit(PixelBlanco, new Point((xx % 64) * tamanoPixel, (yy % 32) * tamanoPixel)); } } } } } surfaceOut.Update(); } void SkipIfKeyDown() { if (teclasPresionadas[MapeoTeclas[V[opcode2]]] == true) PC += 2; } void SkipIfKeyUp() { if (teclasPresionadas[MapeoTeclas[V[opcode2]]] == false) PC += 2; } void AssignFromDelay() { V[opcode2] = delayTimer; } void StoreKey() { for (int i = 0; i < teclasPresionadas.Length; i++) { if (teclasPresionadas[i] == true) { V[opcode2] = i; } } } void AssignToDelay() { delayTimer = V[opcode2]; } void AssignToSound() { soundTimer = V[opcode2]; ejecutaSonido = true; } void AddRegToIndex() { I += V[opcode2]; } void IndexAtFontC8() { I = (V[opcode2] * 0x5); } void IndexAtFontSC8() { // No implementado aún, función de Super Chip-8 } void StoreBCD() { int vx = (int) V[opcode2]; memoria[I] = vx / 100; //centenas memoria[I + 1] = (vx / 10) % 10; //decenas memoria[I + 2] = vx % 10; //unidades } void SaveRegisters() { for (int i = 0; i <= opcode2; i++) { memoria[I++] = V[i]; } } void LoadRegisters() { for (int i = 0; i <= opcode2; i++) { V[i] = memoria[I++]; } } #endregion } }
using System; using System.Windows.Forms; using SdlDotNet.Graphics; using SdlDotNet.Core; using SdlDotNet.Input; //para manejo de teclado namespace Chip8 { public partial class Chip8 : Form { Emulador Emu; private int TargetFPS; private bool estaPausado; private string nombreROM = string.Empty; // variables para cada tecla 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; // constructor public Chip8() { InitializeComponent(); Emu = new Emulador(); this.Text = "Emulador Chip-8"; this.KeyDown += new KeyEventHandler(this.Tecla_Presionada); this.KeyUp += new KeyEventHandler(this.Tecla_Liberada); SdlDotNet.Core.Events.Tick += new EventHandler(Events_Tick); TargetFPS = 60; SdlDotNet.Core.Events.Fps = 60; estaPausado = false; } // Evento clic en opción del menú "Cargar Juego" private void cargarJuegoToolStripMenuItem1_Click(object sender, EventArgs e) { openFileDialog1.FileName = string.Empty; if (openFileDialog1.ShowDialog() == DialogResult.OK) { nombreROM = openFileDialog1.FileName; Reset(); SdlDotNet.Core.Events.Run(); } } // Resetamos el hardware (registros, memoria) y cargamos de nuevo el juego void Reset() { Emu.ResetHardware(); Emu.CargarJuego(nombreROM); } // Game-Loop void Events_Tick(object sender, TickEventArgs e) { this.Text = "Emulador Chip-8 FPS: " + SdlDotNet.Core.Events.Fps; if (estaPausado == false) { Emu.EmulaFrame(); UpdateDisplay(); } } // Actualiza control gráfico con lo que entrega el Surface de la clase Emulador.cs private void UpdateDisplay() { scDisplay.Blit(Emu.SurfaceOut); scDisplay.Update(); } // Evento gatillado al cerrar la aplicación con la "x" de la ventana private void Chip8_FormClosed(object sender, FormClosedEventArgs e) { CerrarAplicacion(); } // Evento gatillado al presionar Pausa private void pausaToolStripMenuItem_Click(object sender, EventArgs e) { if (estaPausado == false) estaPausado = true; else estaPausado = false; } // Evento gatillado al presionar Reset private void resetToolStripMenuItem_Click(object sender, EventArgs e) { Reset(); } // Evento gatillado al presionar Salir private void salirToolStripMenuItem_Click(object sender, EventArgs e) { CerrarAplicacion(); } // Método que termina los eventos y cierra la aplicación void CerrarAplicacion() { SdlDotNet.Core.Events.Close(); SdlDotNet.Core.Events.QuitApplication(); Application.Exit(); } private void Tecla_Presionada(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.NumPad1) { Emu.teclasPresionadas[TECLA_1] = true; } if (e.KeyCode == Keys.NumPad2) { Emu.teclasPresionadas[TECLA_2] = true; } if (e.KeyCode == Keys.NumPad3) { Emu.teclasPresionadas[TECLA_3] = true; } if (e.KeyCode == Keys.NumPad4) { Emu.teclasPresionadas[TECLA_4] = true; } if (e.KeyCode == Keys.Q) { Emu.teclasPresionadas[TECLA_Q] = true; } if (e.KeyCode == Keys.W) { Emu.teclasPresionadas[TECLA_W] = true; } if (e.KeyCode == Keys.E) { Emu.teclasPresionadas[TECLA_E] = true; } if (e.KeyCode == Keys.R) { Emu.teclasPresionadas[TECLA_R] = true; } if (e.KeyCode == Keys.A) { Emu.teclasPresionadas[TECLA_A] = true; } if (e.KeyCode == Keys.S) { Emu.teclasPresionadas[TECLA_S] = true; } if (e.KeyCode == Keys.D) { Emu.teclasPresionadas[TECLA_D] = true; } if (e.KeyCode == Keys.F) { Emu.teclasPresionadas[TECLA_F] = true; } if (e.KeyCode == Keys.Z) { Emu.teclasPresionadas[TECLA_Z] = true; } if (e.KeyCode == Keys.X) { Emu.teclasPresionadas[TECLA_X] = true; } if (e.KeyCode == Keys.C) { Emu.teclasPresionadas[TECLA_C] = true; } if (e.KeyCode == Keys.V) { Emu.teclasPresionadas[TECLA_V] = true; } } private void Tecla_Liberada(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.NumPad1) { Emu.teclasPresionadas[TECLA_1] = false; } if (e.KeyCode == Keys.NumPad2) { Emu.teclasPresionadas[TECLA_2] = false; } if (e.KeyCode == Keys.NumPad3) { Emu.teclasPresionadas[TECLA_3] = false; } if (e.KeyCode == Keys.NumPad4) { Emu.teclasPresionadas[TECLA_4] = false; } if (e.KeyCode == Keys.Q) { Emu.teclasPresionadas[TECLA_Q] = false; } if (e.KeyCode == Keys.W) { Emu.teclasPresionadas[TECLA_W] = false; } if (e.KeyCode == Keys.E) { Emu.teclasPresionadas[TECLA_E] = false; } if (e.KeyCode == Keys.R) { Emu.teclasPresionadas[TECLA_R] = false; } if (e.KeyCode == Keys.A) { Emu.teclasPresionadas[TECLA_A] = false; } if (e.KeyCode == Keys.S) { Emu.teclasPresionadas[TECLA_S] = false; } if (e.KeyCode == Keys.D) { Emu.teclasPresionadas[TECLA_D] = false; } if (e.KeyCode == Keys.F) { Emu.teclasPresionadas[TECLA_F] = false; } if (e.KeyCode == Keys.Z) { Emu.teclasPresionadas[TECLA_Z] = false; } if (e.KeyCode == Keys.X) { Emu.teclasPresionadas[TECLA_X] = false; } if (e.KeyCode == Keys.C) { Emu.teclasPresionadas[TECLA_C] = false; } if (e.KeyCode == Keys.V) { Emu.teclasPresionadas[TECLA_V] = false; } } } }
namespace Chip8 { partial class Chip8 { ///Con esto tenemos un emulador con unas simples opciones que nos permiten cargar ROMs (por ahora solo de Chip-8, faltan algunos ajustes para que soporte ROMs de Super Chip-8), puede Resetear, Pausar, tiene sonido y funciona el teclado. Todo esto gracias a que nos basamos en el código fuente de los capítulos anteriores./// Required designer variable. /// private System.ComponentModel.IContainer components = null; // Control gráfico dentro de la ventana que permite mostrar el juego private SdlDotNet.Windows.SurfaceControl scDisplay; ////// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ////// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Chip8)); this.scDisplay = new SdlDotNet.Windows.SurfaceControl(); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); this.cargarJuegoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.cargarJuegoToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.resetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.salirToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.pausaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); ((System.ComponentModel.ISupportInitialize)(this.scDisplay)).BeginInit(); this.menuStrip1.SuspendLayout(); this.SuspendLayout(); // // scDisplay // this.scDisplay.AccessibleDescription = "SdlDotNet SurfaceControl"; this.scDisplay.AccessibleName = "SurfaceControl"; this.scDisplay.AccessibleRole = System.Windows.Forms.AccessibleRole.Graphic; this.scDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.scDisplay.BackColor = System.Drawing.Color.Black; this.scDisplay.Image = ((System.Drawing.Image)(resources.GetObject("scDisplay.Image"))); this.scDisplay.InitialImage = null; this.scDisplay.Location = new System.Drawing.Point(0, 27); this.scDisplay.Name = "scDisplay"; this.scDisplay.Size = new System.Drawing.Size(640, 320); this.scDisplay.TabIndex = 0; this.scDisplay.TabStop = false; // // menuStrip1 // this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.cargarJuegoToolStripMenuItem, this.pausaToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; this.menuStrip1.Size = new System.Drawing.Size(634, 24); this.menuStrip1.TabIndex = 1; this.menuStrip1.Text = "menuStrip1"; // // cargarJuegoToolStripMenuItem // this.cargarJuegoToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.cargarJuegoToolStripMenuItem1, this.resetToolStripMenuItem, this.salirToolStripMenuItem}); this.cargarJuegoToolStripMenuItem.Name = "cargarJuegoToolStripMenuItem"; this.cargarJuegoToolStripMenuItem.Size = new System.Drawing.Size(60, 20); this.cargarJuegoToolStripMenuItem.Text = "Archivo"; // // cargarJuegoToolStripMenuItem1 // this.cargarJuegoToolStripMenuItem1.Name = "cargarJuegoToolStripMenuItem1"; this.cargarJuegoToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); this.cargarJuegoToolStripMenuItem1.Text = "Cargar Juego"; this.cargarJuegoToolStripMenuItem1.Click += new System.EventHandler(this.cargarJuegoToolStripMenuItem1_Click); // // resetToolStripMenuItem // this.resetToolStripMenuItem.Name = "resetToolStripMenuItem"; this.resetToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.resetToolStripMenuItem.Text = "Reset"; this.resetToolStripMenuItem.Click += new System.EventHandler(this.resetToolStripMenuItem_Click); // // salirToolStripMenuItem // this.salirToolStripMenuItem.Name = "salirToolStripMenuItem"; this.salirToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.salirToolStripMenuItem.Text = "Salir"; this.salirToolStripMenuItem.Click += new System.EventHandler(this.salirToolStripMenuItem_Click); // // pausaToolStripMenuItem // this.pausaToolStripMenuItem.Name = "pausaToolStripMenuItem"; this.pausaToolStripMenuItem.Size = new System.Drawing.Size(50, 20); this.pausaToolStripMenuItem.Text = "Pausa"; this.pausaToolStripMenuItem.Click += new System.EventHandler(this.pausaToolStripMenuItem_Click); // // openFileDialog1 // this.openFileDialog1.FileName = "openFileDialog1"; // // Chip8 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(634, 344); this.Controls.Add(this.menuStrip1); this.Controls.Add(this.scDisplay); this.MainMenuStrip = this.menuStrip1; this.Name = "Chip8"; this.Text = "Chip8"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Chip8_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.scDisplay)).EndInit(); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.MenuStrip menuStrip1; private System.Windows.Forms.ToolStripMenuItem cargarJuegoToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem pausaToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem cargarJuegoToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem resetToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem salirToolStripMenuItem; private System.Windows.Forms.OpenFileDialog openFileDialog1; } }