Help us understand the problem. What is going on with this article?

jsdoでchip8 その5

More than 1 year has passed since last update.

概要

jsdoでchip8やってみた。
マウスで操作してみた。

写真

image

サンプルコード

const hx = (opcode) => ('0000' + opcode.toString(16)).slice(-4);

var chip8 = function(ctx) {
    this.ctx = ctx;
    this.opcode = new Uint16Array(1);
    this.memory = new Uint8Array(4096);
    this.V = new Uint8Array(16);
    this.pc = new Uint16Array(1);
    this.I = new Uint16Array(1);
    this.gfx = new Uint8Array(64 * 32);
    this.delay_timer = new Uint8Array(1);
    this.sound_timer = new Uint8Array(1);
    this.stack = new Uint16Array(16);
    this.sp = new Uint8Array(1);
    this.fontset = new Uint8Array([0xF0, 0x90, 0x90, 0x90, 0xF0, 0x20, 0x60, 0x20, 0x20, 0x70, 
                                   0xF0, 0x10, 0xF0, 0x80, 0xF0, 0xF0, 0x10, 0xF0, 0x10, 0xF0, 
                                   0x90, 0x90, 0xF0, 0x10, 0x10, 0xF0, 0x80, 0xF0, 0x10, 0xF0, 
                                   0xF0, 0x80, 0xF0, 0x90, 0xF0, 0xF0, 0x10, 0x20, 0x40, 0x40, 
                                   0xF0, 0x90, 0xF0, 0x90, 0xF0, 0xF0, 0x90, 0xF0, 0x10, 0xF0, 
                                   0xF0, 0x90, 0xF0, 0x90, 0x90, 0xE0, 0x90, 0xE0, 0x90, 0xE0, 
                                   0xF0, 0x80, 0x80, 0x80, 0xF0, 0xE0, 0x90, 0x90, 0x90, 0xE0, 
                                   0xF0, 0x80, 0xF0, 0x80, 0xF0, 0xF0, 0x80, 0xF0, 0x80, 0x80]);
    this.PC_START = 0x200;
    this.keypad = new Uint8Array(16).fill(0);
    this.initChip8();
};
chip8.prototype.initChip8 = function() {
    this.pc[0] = this.PC_START;
    this.opcode.fill(0);
    this.I.fill(0);
    this.sp.fill(0);
    this.memory.fill(0);
    this.V.fill(0);
    this.stack.fill(0);
    for (var i = 0; i < 80; i++) this.memory[i] = this.fontset[i];
    this.drawFlag = true;
    this.delay_timer.fill(0);
    this.sound_timer.fill(0);
}; 
chip8.prototype.NNN = function(op) {
    return (op & 0x0fff);
};
chip8.prototype.NN = function(op) {
    return (op & 0x00ff);
}
chip8.prototype.N = function(op) {
    return (op & 0x000f);
};
chip8.prototype.X = function(op) {
    return ((op & 0x0F00) >> 8);
};
chip8.prototype.Y = function(op) {
    return ((op & 0x00F0) >> 4);
};
chip8.prototype.L = function(op) {
    return (op & 0xf000);
};
chip8.prototype.disp_clear = function() {
    gfx.fill(0);
};
chip8.prototype.rand = function() {
    return Math.floor(Math.random() * 11);
};
chip8.prototype.inputKey = function(key, n) {
    this.keypad[key] = n;
};
chip8.prototype.emulateCycle = function() {
    this.opcode = (this.memory[this.pc[0]] << 8) | this.memory[this.pc[0] + 1];
    switch (this.L(this.opcode)) 
    {
    case 0x0000:
        switch (this.opcode & 0x00FF)
        {
        case 0x00E0:
            this.disp_clear();
            this.drawFlag = true;
            this.pc[0] += 2;
        break;
        case 0x00EE:
            this.sp[0] -= 1;
            this.pc[0] = this.stack[this.sp[0]];
            this.pc[0] += 2;
        break;
        default:
            //alert(hx(this.opcode));
        }
    break;
    case 0x1000:
        this.pc[0] = this.NNN(this.opcode);
    break;
    case 0x2000:
        this.stack[this.sp[0]] = this.pc[0];
        this.sp[0] += 1;
        this.pc[0] = this.NNN(this.opcode);
    break;
    case 0x3000:
        this.pc[0] += (this.V[this.X(this.opcode)] == this.NN(this.opcode)) ? 4 : 2;
    break;
    case 0x4000:
        this.pc[0] += (this.V[this.X(this.opcode)] != this.NN(this.opcode)) ? 4 : 2;
    break;
    case 0x5000:
        this.pc[0] += (this.V[this.X(this.opcode)] == this.V[this.Y(this.opcode)]) ? 4 : 2;
    break;
    case 0x6000:
        this.V[this.X(this.opcode)] = this.NN(this.opcode);
        this.pc[0] += 2;
    break;
    case 0x7000:
        this.V[this.X(this.opcode)] += this.NN(this.opcode);
        this.pc[0] += 2;
    break;
    case 0x8000:
        switch (this.N(this.opcode))
        {
        case 0x0000:
            this.V[this.X(this.opcode)] = this.V[this.Y(this.opcode)];
        break;
        case 0x0001:
            this.V[this.X(this.opcode)] |= this.V[this.Y(this.opcode)];
            this.V[0xF] = 0;
        break;
        case 0x0002:
            this.V[this.X(this.opcode)] &= this.V[this.Y(this.opcode)];
            this.V[0xF] = 0;
        break;
        case 0x0003:
            this.V[this.X(this.opcode)] ^= this.V[this.Y(this.opcode)];
            this.V[0xF] = 0;
        break;
        case 0x0004:
            this.V[0xF] = this.V[this.Y(this.opcode)] > (0xFF - this.V[this.X(this.opcode)]) ? 1 : 0;
            this.V[this.X(this.opcode)] += this.V[this.Y(this.opcode)];
        break;
        case 0x0005:
            this.V[0xF] = this.V[this.Y(this.opcode)] > this.V[this.X(this.opcode)] ? 0 : 1; 
            this.V[this.X(this.opcode)] -= this.V[this.Y(this.opcode)];
        break;
        case 0x0006:
            this.V[0xF] = this.V[this.X(this.opcode)] & 0x1;
            this.V[this.X(this.opcode)] >>= 0x01;
        break;
        case 0x0007: 
            this.V[0xF] = this.V[this.Y(this.opcode)] < this.V[this.X(this.opcode)] ? 0 : 1; 
            this.V[this.X(this.opcode)] = this.V[this.Y(this.opcode)] - this.V[this.X(this.opcode)];
        break;
        case 0x000E: 
            this.V[0xF] = this.V[this.X(this.opcode)] >> 0x7;
            this.V[this.X(this.opcode)] <<= 0x01;
        break;
        default:
            alert(hx(this.opcode));
        }
        this.pc[0] += 2;
    break;
    case 0x9000:
        this.pc[0] += this.V[this.X(this.opcode)] != this.V[this.Y(this.opcode)] ? 4 : 2;
    break;
    case 0xA000:
        this.I[0] = this.NNN(this.opcode);
        this.pc[0] += 2;
    break;
    case 0xB000:
        this.pc[0] = this.V[0] + this.NNN(this.opcode);
    break;
    case 0xC000:
        this.V[this.X(this.opcode)] = this.NN(this.rand()) & this.NN(this.opcode);
        this.pc[0] += 2;
    break;
    case 0xD000:
    {
        let x = this.V[this.X(this.opcode)];
        let y = this.V[this.Y(this.opcode)];
        let h = this.N(this.opcode);
        this.V[0xF] = 0;
        for (let yline = 0; yline < h; yline++)
        {
            let pixel = this.memory[this.I[0] + yline];
            for (let xline = 0; xline < 8; xline++)
            {
                if ((pixel & (0x80 >> xline)) != 0)
                {
                    if (this.gfx[xline + x + ((y + yline) * 64)] == 1)
                    this.V[0xF] = 1;
                    this.gfx[xline + x + ((y + yline) * 64)] ^= 1;
                }
            }
        }
        this.drawFlag = true;
        this.pc[0] += 2;
    }
    break;
    case 0xE000:
        switch (this.NN(this.opcode)) 
        {
        case 0x009E:
            this.pc[0] += this.keypad[this.V[this.X(this.opcode)]] != 0 ? 4 : 2;
        break;
        case 0x00A1:
            this.pc[0] += this.keypad[this.V[this.X(this.opcode)]] == 0 ? 4 : 2;
        break;
        default:
            alert(hx(this.opcode));
        }
    break;
    case 0xF000:
        switch (this.NN(this.opcode)) 
        {
        case 0x0007:
            this.V[this.X(this.opcode)] = this.delay_timer[0];
            this.pc[0] += 2;
        break;
        case 0x000A:
        {
            var keyPressed0 = false;
            for (var i = 0; i < 16; i++)
            {
                if (this.keypad[i] != 0)
                {
                    this.V[this.X(this.opcode)] = i;
                    keyPressed0 = true;
                }
            }
            if (keyPressed0) this.pc[0] += 2;
        }
        break;
        case 0x0015:
            this.delay_timer[0] = this.V[this.X(this.opcode)];
            this.pc[0] += 2;
        break;
        case 0x0018:
            this.sound_timer[0] = this.V[this.X(this.opcode)];
            this.pc[0] += 2;
        break;
        case 0x001E:
            this.V[0xF] = ((this.I[0] + this.V[this.X(this.opcode)]) > 0xFFF) ? 1 : 0;
            this.I[0] += this.V[this.X(this.opcode)];
            this.pc[0] += 2;
        break;
        case 0x0029:
            this.I[0] = this.V[this.X(this.opcode)] * 0x5;
            this.pc[0] += 2;
        break;
        case 0x0033:
        {
            this.memory[this.I[0] + 0] = this.V[this.X(this.opcode)] / 100;
            this.memory[this.I[0] + 1] = (this.V[this.X(this.opcode)] / 10) % 10;
            this.memory[this.I[0] + 2] = (this.V[this.X(this.opcode)] % 100) % 10;
            this.pc[0] += 2;
        }
        break;
        case 0x0055:
        {
            for (var i = 0; i <= this.X(this.opcode); i++) this.memory[this.I[0] + i] = this.V[i];
            this.I[0] = I[0] + (this.X(this.opcode)) + 1;
            this.pc[0] += 2;
        }
        break;
        case 0x0065:
        {
            for (var i = 0; i <= this.X(this.opcode); i++) this.V[i] = this.memory[this.I[0] + i];
            this.I[0] = this.I[0] + (this.X(this.opcode)) + 1;
            this.pc[0] += 2;
        }
        break;
        default:
            alert(hx(this.opcode));
        }
    break;
    default:
        alert(hx(this.opcode));
    }
    if (this.delay_timer[0] > 0) this.delay_timer[0]--;
    if (this.sound_timer[0] > 0) 
    {
        if (this.sound_timer[0] == 1) console.log('BEEP\n');
        this.sound_timer[0]--;
    }
};
var loop = function() {
    vm.emulateCycle();
    var scale = 4;
    var imageData = vm.ctx.createImageData(64 * scale, 32 * scale);
    var data = imageData.data;
    for (var i = 0; i < data.length; i += 4) 
    {
        var w = 64 * 4 * scale;
        var y = Math.floor(i / w);
        var x = i % w / 4;
        if (vm.gfx[64 * Math.floor(y / scale) + Math.floor(x / scale)])
        {
            data[i + 0] = data[i + 1] = data[i + 2] = 220;
        }
        else
        {
            data[i + 0] = data[i + 1] = data[i + 2] = 20;
        }
        data[i + 3] = 255;
    }
    vm.ctx.putImageData(imageData, 0, 0);
    requestAnimationFrame(loop);
};
var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = "16px 'Courier New'";
var vm = new chip8(ctx);
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://jsrun.it/assets/y/6/t/z/y6tzO', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
    var dv = new DataView(xhr.response);
    for (var i = 0; i < dv.byteLength; i++)
    {
        vm.memory[vm.PC_START + i] = dv.getUint8(i * Uint8Array.BYTES_PER_ELEMENT, false);
    }
    loop();
}
xhr.send();
var $panel = $('#panel');
function mouseClick (el, state) {
    var $el = $(el),
        key = Number($el.data('key'));
    if (state) 
    {
        //alert(key + " keyDown");
        vm.inputKey(key, 1);
    }
    else
    {
        //alert(key + " keyUp");
        vm.inputKey(key, 0);
    }
    $el.toggleClass('click', state);
}
function onKeyDown(e) {
    markKey(e, true);
}
function onKeyUp(e) {
    markKey(e, false);
}
function onMouseDown() {
    mouseClick(this, true);
}
function onMouseUp() {
    mouseClick(this, false);
}
$panel.find('.key').mousedown(onMouseDown).mouseup(onMouseUp).mouseleave(onMouseUp);

成果物

http://jsdo.it/ohisama1/yLTo/

以上。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away