0

I am creating a Chip8 emulator using JavaScript, and I've got it pretty much done. The problem is, it works for the first few seconds, and then gets stuck in an infinite loop of opcodes, and never breaks out.

Check it out: http://output.jsbin.com/mopireb/

It renders the two paddles, the scores, and then when you check the console (where I log all the opcodes and registers, these 3 codes are looping forever.

f007
3000
121a

What am I doing wrong?

Here is my source code (terrible optimization so far, but that's for later):

<div id="memory" />
<input type="text" size="40" onkeypress="trackkey(event)"></input><br />
<canvas id="screen" width="640" height="320" style="border:1px solid #000000;">
</canvas>
<script>
  function trackkey(event) {
  var x = event.which || event.keyCode;
  var y=0;
//    alert(x);
    switch(x){
      case 49://1, 1
        y=1;
        break;
      case 50://2, 2
        y=2;
        break;
      case 51://3, 3
        y=3;
        break;
      case 52://4, 0xc
        y=0xc;
        break;
      case 113://q, 4
        y=4;
        break;
      case 119://w, 5
        y=5;
        break;
      case 101://e, 6
        y=6;
        break;
      case 114://r, 0xD
        y=0xD;
        break;
      case 97://a, 7
        y=7;
        break;
      case 115://s, 8
        y=8;
        break;
      case 100://d, 9
        y=9;
        break;
      case 102://f, 0xe
        y=0xE;
        break;
      case 122://z, 0xA
        y=0xA;
        break;
      case 120://x, 0 
        y=0;
        break;
      case 99://c, 0xB
        y=0xB;
        break;
      case 122://v, 0xF
        y=0xF;
        break;

      default:
        y=50;
        break;
    }
    y-=1;
    if(y!=49){
        pressedKey=y;keyDown=true;
        for(var i=0;i<16;i++){
          if(i!=y){
          key[i]=0;
          }else{key[i]=1;}

  }
    }
  }
  //specs and variables
  var opcode;
  var memory=new Array(4096);
  var V=new Array(16);
  var I;
  var pc;
  var buffer;
  var gfx=new Array(64*32);
  var delay_timer;
  var sound_timer;
  var stack=new Array(16);
  var sp;
  var key=new Array(16);
  var drawFlag;
  var text=[
  0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
  0x20, 0x60, 0x20, 0x20, 0x70, // 1
  0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
  0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
  0x90, 0x90, 0xF0, 0x10, 0x10, // 4
  0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
  0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
  0xF0, 0x10, 0x20, 0x40, 0x40, // 7
  0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
  0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
  0xF0, 0x90, 0xF0, 0x90, 0x90, // A
  0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
  0xF0, 0x80, 0x80, 0x80, 0xF0, // C
  0xE0, 0x90, 0x90, 0x90, 0xE0, // D
  0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
  0xF0, 0x80, 0xF0, 0x80, 0x80  // F
  ];
  //game loop
  main();

  function main(){
    //setupGraphics();
    //setupInput();
    setKeys();
    loadGame();
    initialize();
    drawGraphics();
    setInterval(gameLoop, 16);
  }

  function gameLoop(){

    emulateCycle();
    if(drawFlag){
      drawGraphics();
      setKeys();//store key press state (press and release)
    }

  }

  //store key press state
  function setKeys(){
  }

  //initialize
  function initialize(){
    pc=0x200;
    opcode=0;
    I=0;
    sp=0;
    clear();
    for(var i=0;i<0x10;i++){
      V[i]=0;
    }
    for(var i=0;i<80;i++){
      memory[i]=text[i];
    }
    for(var i=0;i<buffer.length;i++){
      memory[i+512]=buffer[i];
    }

    delay_timer=0;
    sound_timer=0;
  }

  //load program
  function loadGame(){
    buffer=[106,2,107,12,108,63,109,12,162,234,218,182,220,214,110,0,34,212,102,3,104,2,96,96,240,21,240,7,48,0,18,26,199,23,119,8,105,255,162,240,214,113,162,234,218,182,220,214,96,1,224,161,123,254,96,4,224,161,123,2,96,31,139,2,218,182,141,112,192,10,125,254,64,0,125,2,96,0,96,31,141,2,220,214,162,240,214,113,134,132,135,148,96,63,134,2,97,31,135,18,70,2,18,120,70,63,18,130,71,31,105,255,71,0,105,1,214,113,18,42,104,2,99,1,128,112,128,181,18,138,104,254,99,10,128,112,128,213,63,1,18,162,97,2,128,21,63,1,18,186,128,21,63,1,18,200,128,21,63,1,18,194,96,32,240,24,34,212,142,52,34,212,102,62,51,1,102,3,104,254,51,1,104,2,18,22,121,255,73,254,105,255,18,200,121,1,73,2,105,1,96,4,240,24,118,1,70,64,118,254,18,108,162,242,254,51,242,101,241,41,100,20,101,0,212,85,116,21,242,41,212,85,0,238,128,128,128,128,128,128,128,0,0,0,0,0];
  }

  //clear display
  function clear(){
    for(var i=0;i<gfx.length;i++){
      gfx[i]=0;
    }
  }

  //draw
  function draw(x, y, h){
    var coordinates;
    coordinates=y*64+x;
    for(var j=0;j<h;j++){
      for(var i=0;i<8;i++){
        if((coordinates+i+64*j)%64!=0){
          if(gfx[coordinates+i+64*j]==1&&parseInt(parseInt(memory[(I+j)]).toString(2)[i])==0){V[0xF]=1;}else{V[0xF]=0;}
        gfx[coordinates+i+64*j]=parseInt(parseInt(memory[(I+j)]).toString(2)[i]);

        }else{i=8;}
      }
    }
    drawFlag=true;
  }

  //draw graphics
  function drawGraphics(){
    V[0xF]=0;
    for(var i=0;i<gfx.length;i++){
        var c = document.getElementById("screen");
        var ctx = c.getContext("2d");
        if(ctx.getImageData(i%64*10, Math.floor(i/64)*10, 1, 1).data[0]!=0&&gfx[i]!=0){V[0xF]=1;}else{V[0xF]=0;}
        ctx.clearRect(0, 0, ctx.width, ctx.height);
        ctx.beginPath();
        if(gfx[i]==1){ctx.fillStyle = "white";}else{ctx.fillStyle = "black";}
        ctx.fillRect(i%64*10, Math.floor(i/64)*10, 10, 10);
        ctx.stroke();
      }
    drawFlag=false;
  }

  //emulate cycle
  function emulateCycle(){
    console.log(V);
    debugger;
    opcode=0;
    opcode = memory[pc] << 8 | memory[pc + 1];
    switch(opcode&0xF000){
      //0x0NNN not needed
      case 0x0000: //0x00E0 and 0x00EE
        switch(opcode&0x000F){
          case 0x0000:
            clear();//clear display
            drawFlag=true;
            pc+=2;
            break;
          case 0x000E:
            //return from subroutine
            pc=stack[sp-1];
            sp=sp-1;
            pc+=2;
            break;
        }   
        break;
      case 0x1000://goto subroutine
        pc = opcode & 0x0FFF;
        break;
      case 0x2000://call subroutine
        stack[sp]=pc;
        sp++;
        pc=opcode & 0x0FFF;
        break;
      case 0x3000://skip next instruction if Vx==NN
        if(V[(opcode>>8)&0x000F]==opcode&0x00FF){
          pc+=2;
        }
        pc+=2;
        break;
      case 0x4000://skip next instruction if Vx!=NN
        if(V[(opcode>>8)&0x000F]!=opcode&0x00FF){
          pc+=2;
        }
        pc+=2;
        break;
      case 0x5000://skip next instruction if Vx==Vy
        if(V[(opcode>>8)&0x000F]==V[(opcode>>4)&0x000F]){
          pc+=2;
        }
        pc+=2;
        break;
      case 0x6000://set Vx to NN
        V[(opcode>>8)&0x000F]=opcode&0x00FF;
        //alert(V[(opcode>>8)&0x000F]);
        pc+=2;
        break;
      case 0x7000://Vx+=NN
        V[(opcode>>8)&0x000F]+=opcode&0x00FF;
        pc+=2;
        break;
      case 0x800F:
        switch(opcode&0x000F){
          case 0x0000://Vx=Vy
            V[(opcode>>8)&0x000F]=V[(opcode>>4)&0x000F];
            pc+=2;
            break;
          case 0x0001://Vx=Vx|Vy
            V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]|V[(opcode>>4)&0x000F];
            pc+=2;
            break;
          case 0x0002://Vx=Vx&Vy
            V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]&V[(opcode>>4)&0x000F];
            pc+=2;
            break;
          case 0x0003://Vx=Vx^Vy
            V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]^V[(opcode>>4)&0x000F];
            pc+=2;
            break;
          case 0x0004://Vx+=Vy
            if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
              V[0xF] = 1; //carry
            }else{
              V[0xF] = 0;
              V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4];
            } 
            pc+=2;
            break;
          case 0x0005://Vx-=Vy
            if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
              V[0xF] = 1; //carry
            }else{
              V[0xF] = 0;
              V[(opcode & 0x0F00) >> 8] -= V[(opcode & 0x00F0) >> 4];
            } 
            pc+=2;
            break;
          case 0x0006://Vx=Vy=Vy>>1
            V[0xF]=V[(opcode>>4)&0x000F]&0x000F;
            V[(opcode>>4)&0x000F]=V[(opcode>>4)&0x000F]>>1;
            V[(opcode>>8)&0x000F]=(opcode>>4)&0x000F;
            pc+=2;
            break;
          case 0x0007://Vx=Vy-Vx
            if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
              V[0xF] = 1; //carry
            }else{
              V[0xF] = 0;
              V[(opcode & 0x00F0) >> 4]=V[(opcode & 0x0F00) >> 8]-V[(opcode & 0x00F0) >> 4];
            } 
            pc+=2;
            break;
          case 0x000E://Vx=Vy=Vy<<1
            V[0xF]=V[(opcode & 0x0F00) >> 8] >> 7;
            V[(opcode>>4)&0x000F]=V[(opcode>>4)&0x000F]<<1;
            V[(opcode>>8)&0x000F]=V[(opcode>>4)&0x000F];
            pc+=2;
            break;
        }
        break;
      case 0x9000://if(Vx!=Vy){skip}
        if(V[(opcode>>8)&0x000F]!=V[(opcode>>4)&0x000F]){
          pc+=2;
        }
        pc+=2;
      case 0xA000://I=NNN
        I=opcode&0x0FFF;
        pc+=2;
        break;
      case 0xB000://I=NNN+V0
        pc=(opcode&0x0FFF)+V[0];
        break;
      case 0xC000://Vx=random&NN
        V[(opcode>>8)&0x000F]=Math.floor((Math.random() * 255))&((opcode)&0x00FF);
        pc+=2;
        break;
      case 0xD000://DXYN draw(Vx, Vy, N)
        draw(V[(opcode>>8)&0x000F], V[(opcode>>4)&0x000F], (opcode)&0x000F);
        pc+=2;
        break;
      case 0xE000://keypad
        switch(opcode & 0x00FF){// EX9E: Skips the next instruction if the key stored in VX is pressed
          case 0x009E:
            if(key[V[(opcode & 0x0F00) >> 8]] != 0){
              pc+=2;
            }else{}
            pc+=2;
            break;
          case 0x00A1:
            if(key[V[(opcode & 0x0F00) >> 8]] == 0){
              pc+=2;
            }else{}
            pc+=2;
            break;
        }
        break;
      case 0xF000:
        switch(opcode & 0x00FF){
          case 0x0007:
            V[(opcode>>8)&0x000F]=delay_timer;
            pc+=2;
            break;
          case 0x000A:
            if(keyDown==true){
                V[(opcode>>8)&0x000F]=pressedKey;pc+=2;
            }else{alert("PAUSE");}
            break;
          case 0x0015:
            delay_timer=V[(opcode>>8)&0x000F];
            pc+=2;
            break;
          case 0x0018:
            sound_timer=V[(opcode>>8)&0x000F];
            pc+=2;
            break;
          case 0x001E:
            I+=V[(opcode>>8)&0x000F]; 
            pc+=2;
            break;
          case 0x0029:
            I=V[(opcode>>8)&0x000F]*5;
            pc+=2;
            break;
          case 0x0033:
            memory[I]     = V[(opcode & 0x0F00) >> 8] / 100;
            memory[I + 1] = (V[(opcode & 0x0F00) >> 8] / 10) % 10;
            memory[I + 2] = (V[(opcode & 0x0F00) >> 8] % 100) % 10;
            pc += 2;
            break;
          case 0x0055:
            for (var i = 0; i <= ((opcode & 0x0F00) >> 8); i+=2){
              memory[I + i] = V[i];
              I++;
            }
            pc+=2;
            break;
          case 0x0065:

            for (var i = 0; i <= ((opcode & 0x0F00) >> 8); i+=2){
              V[i] = (memory[I+i]<<8)&memory[I+(i+1)];
              I++;
            }
            pc += 2;
            break;
        }     
        break;
      default:
        console.log("UNSUPPORTED OPCODE- "+(opcode).toString(16));console.log("");
        break;
      }
    console.log(opcode.toString(16));
    if(delay_timer > 0){delay_timer-=1;}
    if(sound_timer > 0){if(sound_timer == 1){console.log("BEEP!");sound_timer-=1;}}
    keyDown=false;
    pressedKey=null;
  }

</script>
Kento Nishi
  • 578
  • 1
  • 9
  • 24
  • clearly `skip next instruction if Vx==NN` is never true ... `0xf007` results in `V[(opcode>>8)&0x000F] = delay_timer` ... however, no subsequent instruction will modify `V[(opcode>>8)&0x000F]` so unless it is initially set to `NN` that statement will never be true ... note: changing `delay_timer` wont change `V[(opcode>>8)&0x000F]` – Jaromanda X Mar 20 '18 at 01:40
  • So... how do I fix case 0x3000://skip next instruction if V[x]==NN? – Kento Nishi Mar 20 '18 at 02:21
  • well, you need to do something with `V[(opcode>>8)&0x000F]` - not sure what ... I mean, it's your emulator :p – Jaromanda X Mar 20 '18 at 02:29

0 Answers0