CodeSlinger
New member
Good job mate, looks impressive. Im looking forward to viewing the source code. If you're having trouble getting a host for your code then I'll happily host it for you. Just PM me if you want.
no pants :yucky:
using System;
using System.Drawing;
using SdlDotNet;
using SdlDotNet.Sprites;
using System.Runtime.InteropServices; //beep
using System.IO;
namespace Chip8_2
{
public class cap1
{
#region variables emulador
const int multiplicadorPixel = 5;
Surface srf = null;
int frames = 0;
const int START_ADDR = 0x200;
const int MEMORY_SIZE = 0xFFF;
const int MAX_KEYS = 16;
const int MAX_STACKSIZE = 16;
const int MAX_REGISTERS = 16;
const int RES_X = 64; //resolucion original 64x32
const int RES_Y = 32;
const int CHIP8_FNT_Y = 5;
const int SPRITE_X = 8;
int opCode1 = 0;
int opCode2 = 0; //X
int opCode3 = 0; //Y
int opCode4 = 0;
int[,] arrcScreen = new int[RES_X, RES_Y];
//Chip 8 tiene 2 timers: Delay Timer y Sound Timer
int cDelayTimer;
int cSoundTimer;
/// msec per timer tick, default is about 16
/// </summary>
public const int TIMER_MSEC_PER_TICK = 16;
//Used to control the timers in the Chip
DateTime _lastTime = DateTime.Now;
//high resolution delay timer implementation
private double _timerDelay;
// Delay timer, each value represents is about 16 msec
public byte TimerDelay
{
get { return (byte)_timerDelay; }
set { _timerDelay = value; }
}
// Sound timer - the speaker beeps if non-zero
// Each value is about 16 msec
public byte TimerSound
{
get { return (byte)_timerSound; }
set { _timerSound = value; }
}
//high resolution sound timer implementation
private double _timerSound;
DateTime fps_inicio = DateTime.Now;
[DllImport("Kernel32.dll")] //para beep
public static extern bool Beep(UInt32 frequency, UInt32 duration);
int[] arrcMemory = new int[MEMORY_SIZE];
int[] V = new int[MAX_REGISTERS];
int opCode;
int sPC;
int sI;
int sSP;
int[] sStack = new int[MAX_STACKSIZE];
int KK;
int[] arrcFontC8 = {
0xF0, 0x90, 0x90, 0x90, 0xF0, // Values For 0
0x60, 0xE0, 0x60, 0x60, 0xF0, // Values For 1
0x60, 0x90, 0x20, 0x40, 0xF0, // Values For 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // Values For 3
0x90, 0x90, 0xF0, 0x10, 0x10, // Values For 4
0xF0, 0x80, 0x60, 0x10, 0xE0, // Values For 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // Values For 6
0xF0, 0x10, 0x10, 0x10, 0x10, // Values For 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // Values For 8
0xF0, 0x90, 0xF0, 0x10, 0x10, // Values For 9
0x60, 0x90, 0xF0, 0x90, 0x90, // Values For A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // Values For B
0x70, 0x80, 0x80, 0x80, 0x70, // Values For C
0xE0, 0x90, 0x90, 0x90, 0xE0, // Values For D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // Values For E
0xF0, 0x90, 0xF0, 0x80, 0x80 // Values For F
};
private bool[] KEYS_PRESSED = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,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;
// mapea el teclado qwerty al teclado del chip8
private byte[] KeyMap = {
0x01,0x02,0x03,0x0C,
0x04,0x05,0x06,0x0D,
0x07,0x08,0x09,0x0E,
0x0A,0x00,0x0B,0x0F };
// desmapea el teclado del chip8 al teclado qwerty
private byte[] KeyUnMap = {
0x0D,0x00,0x01,0x02,
0x04,0x05,0x06,0x08,
0x09,0x0A,0x0C,0x0E,
0x03,0x07,0x0B,0x0F };
#endregion
public cap1()
{
//resoluci n
Video.SetVideoModeWindow(RES_X*multiplicadorPixel, RES_Y*multiplicadorPixel);
// Limpar la pantalla (dejarla negra)
Video.Screen.Fill(Color.Black);
Video.WindowCaption = "Emulador Chip8 v0.01 - FPS: ?";
srf = new Surface(RES_X*multiplicadorPixel, RES_Y*multiplicadorPixel);
srf.Fill(Color.Black);
ResetHardware();
CargarROM("PONG"); //PONG PONG2
//_lastTime = DateTime.Now;
Events.Tick += new TickEventHandler(Events_Tick);
Events.Quit += new QuitEventHandler(Events_Salir);
}
private void Events_Salir(object sender, SdlDotNet.QuitEventArgs e)
{
Events.QuitApplication();
}
public void Run()
{
Events.KeyboardDown +=new KeyboardEventHandler(this.KeyboardPressed);
Events.KeyboardUp +=new KeyboardEventHandler(this.KeyboardReleased);
Events.Run();
}
private void KeyboardPressed(object sender, KeyboardEventArgs e)
{
//Salimos del juego con tecla Escape
if(e.Key == Key.Escape)
{
Events.QuitApplication();
}
// activa la tecla presionada
if(e.Key == Key.One) {KEYS_PRESSED[TECLA_1] = true;}
if(e.Key == Key.Two) {KEYS_PRESSED[TECLA_2] = true;}
if(e.Key == Key.Three) {KEYS_PRESSED[TECLA_3] = true;}
if(e.Key == Key.Four) {KEYS_PRESSED[TECLA_4] = true;}
if(e.Key == Key.Q) {KEYS_PRESSED[TECLA_Q] = true;}
if(e.Key == Key.W) {KEYS_PRESSED[TECLA_W] = true;}
if(e.Key == Key.E) {KEYS_PRESSED[TECLA_E] = true;}
if(e.Key == Key.R) {KEYS_PRESSED[TECLA_R] = true;}
if(e.Key == Key.A) {KEYS_PRESSED[TECLA_A] = true;}
if(e.Key == Key.S) {KEYS_PRESSED[TECLA_S] = true;}
if(e.Key == Key.D) {KEYS_PRESSED[TECLA_D] = true;}
if(e.Key == Key.F) {KEYS_PRESSED[TECLA_F] = true;}
if(e.Key == Key.Z) {KEYS_PRESSED[TECLA_Z] = true;}
if(e.Key == Key.X) {KEYS_PRESSED[TECLA_X] = true;}
if(e.Key == Key.C) {KEYS_PRESSED[TECLA_C] = true;}
if(e.Key == Key.V) {KEYS_PRESSED[TECLA_V] = true;}
}
private void KeyboardReleased(object sender, KeyboardEventArgs e)
{
// activa la tecla presionada
if(e.Key == Key.One) {KEYS_PRESSED[TECLA_1] = false;}
if(e.Key == Key.Two) {KEYS_PRESSED[TECLA_2] = false;}
if(e.Key == Key.Three) {KEYS_PRESSED[TECLA_3] = false;}
if(e.Key == Key.Four) {KEYS_PRESSED[TECLA_4] = false;}
if(e.Key == Key.Q) {KEYS_PRESSED[TECLA_Q] = false;}
if(e.Key == Key.W) {KEYS_PRESSED[TECLA_W] = false;}
if(e.Key == Key.E) {KEYS_PRESSED[TECLA_E] = false;}
if(e.Key == Key.R) {KEYS_PRESSED[TECLA_R] = false;}
if(e.Key == Key.A) {KEYS_PRESSED[TECLA_A] = false;}
if(e.Key == Key.S) {KEYS_PRESSED[TECLA_S] = false;}
if(e.Key == Key.D) {KEYS_PRESSED[TECLA_D] = false;}
if(e.Key == Key.F) {KEYS_PRESSED[TECLA_F] = false;}
if(e.Key == Key.Z) {KEYS_PRESSED[TECLA_Z] = false;}
if(e.Key == Key.X) {KEYS_PRESSED[TECLA_X] = false;}
if(e.Key == Key.C) {KEYS_PRESSED[TECLA_C] = false;}
if(e.Key == Key.V) {KEYS_PRESSED[TECLA_V] = false;}
}
[STAThread]
static void Main()
{
cap1 juego = new cap1();
juego.Run();
}
#region emulador
private void Events_Tick(object sender, TickEventArgs e)
{
Emula();
}
public void Emula()
{
FetchOpcode();
ExecuteOpcode();
//beep
if (cSoundTimer > 0)
Beep(500, 1);
//DateTime now = DateTime.Now;
// calculate how much time passed since the last update
// double elapsedMsec = (now - _lastTime).TotalMilliseconds;
double tiempoPasado = (DateTime.Now - fps_inicio).TotalMilliseconds;
if (tiempoPasado > 1000) // cada un segundo grabo los Frames, asi calculo los frames por segundo (deber퀀an ser 60)
{
Video.WindowCaption = "Emulador Chip8 v0.01 - FPS: " + frames.ToString();
frames = 0;
fps_inicio = DateTime.Now;
}
if (cDelayTimer > 0) { cDelayTimer--; }
if (cSoundTimer > 0) { cSoundTimer--; }
}
void ResetHardware()
{
// Resetting Timers.
cDelayTimer = 0x0;
cSoundTimer = 0x0;
// Resetting Pointing Variables.
opCode = 0x0;
sPC = START_ADDR;
sSP = 0x0;
sI = 0x0;
// Clearing Memory.
for (int nAddr = 0; nAddr < MEMORY_SIZE; nAddr++)
{
arrcMemory[nAddr] = 0x0;
}
// Clearing Registers.
for (int nCurrReg = 0; nCurrReg < MAX_REGISTERS; nCurrReg++)
{
arrcMemory[nCurrReg] = 0x0;
}
// Clearing Stack.
for (int nCurrItem = 0; nCurrItem < MAX_STACKSIZE; nCurrItem++)
{
sStack[nCurrItem] = 0x0;
}
// Load Fontset To Memory.
LoadFontset(); //Las fuentes se usan para pintar palabras, por ej, los marcadores del juego PONG [0-0]
}
void LoadFontset()
{
for (int nIndex = 0; nIndex < 80; nIndex++)
{
arrcMemory[nIndex] = arrcFontC8[nIndex];
}
}
bool CargarROM(string ruta_rom)
{
FileStream rom = new FileStream(@ruta_rom,FileMode.Open);
if (rom.Length == 0)
{
Console.Write("Error: Archivo daကado o vac퀀o");
return false;
}
for (int i = 0; i<rom.Length; i++)
arrcMemory[START_ADDR + i] = (byte) rom.ReadByte(); //comenzamos de 200
rom.Close();
return true;
}
void FetchOpcode()
{
// Get Opcode From Memory (2 Bytes).
opCode = arrcMemory[sPC] << 8 | arrcMemory[sPC+1];
// Advance Program Counter To Next Opcode.
sPC += 2;
}
void ExecuteOpcode()
{
// Logging Opcode to Log File.
//logOpcode(sPC - 2,opCode);
KK = (opCode & 0x00FF);
//REG_Y = (opCode & 0x00F0) >> 4;
//REG_X = (opCode & 0x0F00) >> 8;
// separo el opCode en 4 bytes
opCode1 = (opCode & 0xF000) >> 12;
opCode2 = (opCode & 0x0F00) >> 8; //X
opCode3 = (opCode & 0x00F0) >> 4; //Y
opCode4 = (opCode & 0x000F) >> 0;
// Identifying Opcodes by Their Left-Most Figure.
switch (opCode1)
{
// 0xxx Opcodes.
case (0x0):
{
// Identifying 0xxx Opcodes.
switch (opCode & 0x0FFF)
{
// Opcode 00E0: Clear Screen.
case (0x00E0):
{
ClearScreen();
break;
}
// Opcode 00EE: Return From Subroutine.
case (0x00EE):
{
ReturnFromSub();
break;
}
}
break;
}
// 1xxx Opcodes.
case (0x1):
{
// Opcode 1NNN: Jump To Address NNN.
JumpToAddr();
break;
}
// 2xxx Opcodes.
case (0x2):
{
// Opcode 2NNN: Call Subroutine At Address NNN.
CallSub();
break;
}
// 3xxx Opcodes.
case (0x3):
{
// Opcode 3XKK: Skip Next Instruction If VX == KK
SkipIfEql();
break;
}
// 4xxx Opcodes.
case (0x4):
{
// Opcode 4XKK: Skip Next Instruction If VX != KK
SkipIfNotEql();
break;
}
// 5xxx Opcodes.
case (0x5):
{
// Opcode 5XY0: Skip Next Instruction If VX == VY
SkipIfRegEql();
break;
}
// 6xxx Opcodes.
case (0x6):
{
// Opcode 6XKK: Assign Number KK To Register X.
AssignNumToReg();
break;
}
// 7xxx Opcodes.
case (0x7):
{
// Opcode 7XKK: Add Number KK To Register X.
AddNumToReg();
break;
}
// 8xxx Opcodes.
case (0x8):
{
// Identifying 8xxx Opcodes.
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;
}
// 9xxx Opcodes.
case (0x9):
{
// Opcode 9XY0: Skip Next Instruction If VX != VY
SkipIfRegNotEql();
break;
}
// Axxx Opcodes.
case (0xA):
{
// Opcode ANNN: Set Index Register To Address NNN.
AssignIndexAddr();
break;
}
// Bxxx Opcodes.
case (0xB):
{
// Opcode BNNN: Jump To NNN + V0.
JumpWithOffset();
break;
}
// Cxxx Opcodes.
case (0xC):
{
// Opcode CXKK: Assign Bitwise AND Of Random Number & KK To Register X.
RandomANDnum();
break;
}
// Dxxx Opcodes.
case (0xD):
{
// Opcode DXYN: Draw Sprite To The Screen.
DrawSprite();
break;
}
// Exxx Opcodes.
case (0xE):
{
// Identifying Exxx Opcodes.
switch (KK)//opCode & 0x00FF
{
// Opcode 0xEX9E: Skip Next Instruction If Key In VX Is Pressed.
case (0x9E):
{
SkipIfKeyDown();
break;
}
// Opcode 0xEXA1: Skip Next Instruction If Key In VX Is NOT Pressed.
case (0xA1):
{
SkipIfKeyUp();
break;
}
}
break;
}
// Fxxx Opcodes.
case (0xF):
{
// Identifying Fxxx Opcodes.
switch (KK)//opCode & 0x00FF
{
// 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;
}
}
}
// --------------------------------------------------------------------------------------
void ClearScreen()
{
Video.Screen.Fill(Color.Black);
}
void ReturnFromSub()
{
// Going To Last Stack Item.
sSP--;
// Pointing Next Instruction To The Saved Position In The Stack.
sPC = sStack[sSP];
}
void JumpToAddr()
{
// Jumping To Given Address.
sPC = (opCode & 0x0FFF);
}
void CallSub()
{
// Saving Current Program Counter.
sStack[sSP] = sPC;
sSP++;
// Jumping To Subroutine At Given Address.
sPC = (opCode & 0x0FFF);
}
void SkipIfEql()
{
// Check If The Register Value Equals The Given Value.
if (V[opCode2] == KK)
{
// Skip Next Instruction.
sPC += 2;
}
}
void SkipIfNotEql()
{
// Check If The Register Value Differs From The Given Value.
if (V[opCode2] != KK)
{
// Skip Next Instruction.
sPC += 2;
}
}
void SkipIfRegEql()
{
// Check If The X Register Value Equals The Y Register Value.
if (V[opCode2] == V[opCode3])
{
// Skip Next Instruction.
sPC += 2;
}
}
void AssignNumToReg()
{
// Assign Given Number To Register.
V[opCode2] = KK;
}
private char getLower(int number)
{
return (char)(number&0xFF);
}
void AddNumToReg()
{
// Add Given Number To Register.
V[opCode2] += KK;
}
// --------------------------------------------------------------------------------------
void AssignRegToReg()
{
// Assign Register Y To Register X.
V[opCode2] = V[opCode3];
}
void RegisterOR()
{
// Assign Mutual OR On Registers To X.
V[opCode2] |= V[opCode3];
}
void RegisterAND()
{
// Assign Mutual AND On Registers To X.
V[opCode2] &= V[opCode3];
}
void RegisterXOR()
{
// Assign Mutual XOR On Registers To X.
V[opCode2] ^= V[opCode3];
}
void AddRegToReg()
{
//8XY4 Realiza esto: VX = VX + VY, VF = carry
// Keep Carry Indication In Register F.
V[0xF] = (V[opCode2] + V[opCode3]) >> 8;
// Add Register Y To Register X.
V[opCode2] += V[opCode3];
}
void SubRegFromReg()
{
// Saving Negated Borrow Indication In Register F.
if (V[opCode2] >= V[opCode3])
{
V[0xF] = 0x1;
}
else
{
V[0xF] = 0x0;
}
// Subtracting Register Y From Register X.
V[opCode2] -= V[opCode3];
}
void ShiftRegRight()
{
V[0xF] = V[opCode2] & 0x1;
V[opCode2] >>= 1;
}
void ReverseSubRegs()
{
if (V[opCode2] <= V[opCode3])
{
V[0xF] = 0x1;
}
else
{
V[0xF] = 0x0;
}
}
void ShiftRegLeft()
{
V[0xF] = V[opCode2] & 0x10;
V[opCode2] <<= 1;
}
// --------------------------------------------------------------------------------------
void SkipIfRegNotEql()
{
// Check If The X Register Value Differs From The Y Register Value.
if (V[opCode2] != V[opCode3])
{
// Skip Next Instruction.
sPC += 2;
}
}
void AssignIndexAddr()
{
// Set Index Register To Point To Address NNN.
sI = opCode & 0x0FFF;
}
void JumpWithOffset()
{
// Jump To Address NNN + Offset From Register 0.
sPC = (opCode & 0x0FFF) + V[0x0];
}
void RandomANDnum()
{
// Generating Random Number With Given Number.
Random r = new Random((int)DateTime.Now.Ticks);
int rnd = r.Next();
if (rnd < 0) rnd = rnd*-1; //siempre positivo
V[opCode2] = rnd & 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()
{
// Resetting Collision Detection.
V[0xF] = 0x0;
if ((opCode & 0x000F) == 0) //opCode & 0x000F =opcode4
{
// Draw SCHIP8 Sprite Of Size 16x16.
// * Not Implanted Yet :P *
}
else
{
// Draw CHIP8 Sprite Of Size 8xN
for (int nSpriteY = 0; nSpriteY < opCode4; nSpriteY++)
{
for (int nSpriteX = 0; nSpriteX < SPRITE_X; nSpriteX++)
{
int x = (arrcMemory[sI + nSpriteY] & (0x80 >> nSpriteX));
if ( x != 0)
{
// Checking For Collision (Overwrite Pixel).
int xx = (V[opCode2] + nSpriteX);
int yy = (V[opCode3] + nSpriteY);
//grabaLog ("xx=" + xx.ToString() + " xx%64=" + (xx%64).ToString() + " yy=" + yy.ToString() + " yy%32=" + (yy%32).ToString());
if (arrcScreen[xx % 64, yy % 32] == 1) //% resto o modulo de una division, ejemplo 32%64=32, 0%32=0, 1%32=1, 31%32=31, 32%32=0, 33%32=1 (aqui se caia ya que llegaba a 32 y el max que puede alcanzar es 31)
V[0xF] = 1;
arrcScreen[xx % 64, yy % 32] ^= 1; //XOR 0^1=1, 1^0=1, 1^1 =0, 0^0=0
}
}
}
}
UpdateScreen();
}
void SkipIfKeyDown()
{
if(KEYS_PRESSED[KeyUnMap[V[opCode2]]] == true)
sPC += 2;
}
void SkipIfKeyUp()
{
if(KEYS_PRESSED[KeyUnMap[V[opCode2]]] == false)
sPC += 2;
}
void AssignFromDelay()
{
V[opCode2] = cDelayTimer;
}
void StoreKey()
{
for (int nKey = 0; nKey < KEYS_PRESSED.Length; nKey++)
{
if (KEYS_PRESSED[nKey] == true)
{
V[opCode2] = nKey;
}
}
}
void AssignToDelay()
{
cDelayTimer = V[opCode2];
}
void AssignToSound()
{
cSoundTimer = V[opCode2];
}
void AddRegToIndex()
{
sI += V[opCode2];
}
void IndexAtFontC8()
{
sI = (V[opCode2] * 0x5);
}
void IndexAtFontSC8()
{
// Not Implemanted yer, SCHIP8 Function.
}
void StoreBCD()
{
int nRegister = (int) V[opCode2];
arrcMemory[sI] = nRegister / 100;
arrcMemory[sI + 1] = (nRegister / 10) % 10;
arrcMemory[sI + 2] = nRegister % 10;
}
void SaveRegisters()
{
for (int nIndex = 0; nIndex <= opCode2; nIndex++)
{
arrcMemory[sI++] = V[nIndex];
}
sI += 1;
}
void LoadRegisters()
{
for (int nIndex = 0; nIndex <= opCode2; nIndex++)
{
V[nIndex] = arrcMemory[sI++];
}
sI += 1;
}
void UpdateScreen()
{
int nDrawX;
int nDrawY;
for (nDrawX = 0; nDrawX < RES_X; nDrawX++)
{
for (nDrawY = 0; nDrawY < RES_Y; nDrawY++)
{
if (arrcScreen[nDrawX, nDrawY] != 0)
{
Box b = new Box(new Point(nDrawX, nDrawY), new Size(1,1));
srf.DrawFilledBox(b, Color.White);
}
else
{
Box b = new Box(new Point(nDrawX, nDrawY), new Size(1,1));
srf.DrawFilledBox(b, Color.Black);
}
}
}
Surface srf_back = new Surface(srf);
srf.Scale(multiplicadorPixel);
Video.Screen.Blit(srf);
srf = srf_back; //para no ir Scalando indefinidamente
Video.Screen.Update();
frames ++; //para el calculo de FPS
}
#endregion
public void grabaLog(string data)
{
Console.WriteLine(data);
}
}
}
I was wondering if there is an easy way to cut down on the flickering? I really don't know much directX at this ponit.
No reliable way. It's a flaw of the hardware itself - it simply lacked instructions for collision, so the games tend to draw on already drawn parts every frame or so, and then checks the collision bits.
There might be a way to "hack" this system by, for example, guessing if the game did it to know if there was a collision or not, and if it was merely for checking collision, then skipping the erasing part and the subsequent draw to redraw the part that was erased.
But sometimes games just wants to erase pixels on the screen, which can also be done via the draw opcode.
So no, no reliable way.
I used a game-specific hack.Hey Doom I'm writing a silverlight Chip-8 Emu to learn to write emulators because I want to write a SNES or NES silverlight emu. My Chip-8 is working great on many games but I noticed on PONG some flickering. How did you fix that on yours.
Sorry no. I don't program Silverlight. I only do C/C++ (preferably C++).Thanks so much for your help. Also do you have VS2008 and Silverlight 3 Beta. I'd love to let you look over my source but it's in Silverlight 3 Beta. Thanks again.
I can't remember 100%, but I believe I fixed the number of opcodes per second or some such. I would have to re-read and re-test the code to understand it nowAlso how do you set your timing for the CPU cycle, it works so great on every game. On mine some games seem slow and some seem fast. Thanks.
No idea. It could be a thousand things wrong.Also do you have any idea why blinky is not working? It drawing some really weird stuff.
It has to do with the timing, but again, I'm not sure how I did it, exactly.Also in your source it looks like you update your delay register only after every 20 op codes is this correct? or what am I missing when I read your source. Thanks.
public void DRW_Vx_Vy_nibble()
{
int x=(op & 0xf00)>>8;
int y=(op & 0xf0)>>4;
int _x=V[x],_y=V[y];
int n=(op & 0xf);
String _I=Integer.toHexString(I);
String tmp=" x=V"+x+" y=V"+y;
Debug("Display "+n+"-byte sprite starting at memory location 0x"+_I+" at ("+V[x]+", "+V[y]+"), "+tmp+" set VF = collision.");
PC+=2;
/*
System.out.print("data to be written: ");
for(int k=0;k<x;k++)
{
System.out.print(MEMORY[I]+" ");
}
System.out.println();
for(int _y=0;_y<n;_y++)
{
DrawPixelRow(V[x],V[y]+_y,MEMORY[I+_y],x,y);
//System.out.println();
}
*/
int pixel=0;
for(int i=0;i<n;i++)
{
int data=MEMORY[I+i];
for(int j=0;j<8;j++)
{
if((data & (0x80 >> j))!=0)
{
if(pixel==1)
V[0xf]=1;
pixel=video[_x+j][_y+i];
video[_x+j][_y+i]^=1;
} //End child for
}//End parent for
}//End draw method
//video[100][100]=1;
this.VideoChanged=true;
}