LoginSignup
0
0

More than 5 years have passed since last update.

概要

jsdoでchip8やってみた。
開発環境を整備してみた。
アセンブラとエミュを合体した。

写真

image

サンプルコード

function _arrayBufferToBase64(buffer) {
    var binary = '';
    var bytes = new Uint8Array(buffer);
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) 
    {
        binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
}
function tob(url) {
    var bin = atob(url);
    var buffer = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++)
    {
        buffer[i] = bin.charCodeAt(i);
    }
    var blob = new Blob([buffer.buffer], {
        type : 'application/octet-binary'
    });
    var blob_url = window.URL.createObjectURL(blob);
    return blob_url;
}
function run() {
    var c1, 
        c2, 
        c3, 
        a, 
        i, 
        j;
    var pc = 0;
    var la = new Array(10);
    var r = new Array(100);
    var src = document.getElementById('src0').value;
    var m = src.split("\n");
    for (j = 0; j < m.length; j++)
    {
        var cm = m[j].split("  ");
        if (cm[0] != "")
        {
            la[cm[0]] = pc + 2;
        }
        switch (cm[1])
        {
        case "disp":
            var x = 0;
            switch (cm[2])
            {
            case "v0":
                x = 0;
            break;
            case "v1":
                x = 1;
            break;        
            case "v2":
                x = 2;
            break;        
            case "v3":
                x = 3;
            break;        
            case "v4":
                x = 4;
            break;        
            case "v5":
                x = 5;
            break;
            case "v6":
                y = 6;
            break;            
            case "v7":
                x = 7;
            break;        
            case "v8":
                x = 8;
            break;        
            case "v9":
                x = 9; 
            break;        
            case "v10":
                x = 10;
            break;        
            case "v11":
                x = 11;
            break;        
            case "v12":
                x = 12;
            break;        
            case "v13":
                x = 13;
            break;        
            case "v14":
                x = 14;
            break;        
            case "v15":
                x = 15;
            break;        
            }
            var y = 0;
            switch (cm[3])
            {
            case "v0":
                y = 0;
            break;        
            case "v1":
                y = 1;
            break;        
            case "v2":
                y = 2;
            break;        
            case "v3":
                y = 3;
            break;        
            case "v4":
                y = 4;
            break;        
            case "v5":
                y = 5;
            break;
            case "v6":
                y = 6;
            break;            
            case "v7":
                y = 7;
            break;        
            case "v8":
                y = 8;
            break;        
            case "v9":
                y = 9;
            break;        
            case "v10":
                y = 10;
            break;        
            case "v11":
                y = 11;
            break;        
            case "v12":
                y = 12;
            break;        
            case "v13":
                y = 13;
            break;        
            case "v14":
                y = 14;
            break;        
            case "v15":
                y = 15;
            break;        
            }    
            r[pc] = 0xD0 + x;
            pc++;
            r[pc] = y * 16 + Number(cm[4]);
            pc++;
        break;
        case "add":
            if (cm[2] == "I")
            {
                var x = 0;
                switch (cm[3])
                {
                case "v0":
                    x = 0;
                break;        
                case "v1":
                    x = 1;
                break;        
                case "v2":
                    x = 2;
                break;        
                case "v3":
                    x = 3;
                break;        
                case "v4":
                    x = 4;
                break;        
                case "v5":
                    x = 5;
                break;  
                case "v6":
                    y = 6;
                break;            
                case "v7":
                    x = 7;
                break;        
                case "v8":
                    x = 8;
                break;        
                case "v9":
                    x = 9;
                break;        
                case "v10":
                    x = 10;
                break;        
                case "v11":
                    x = 11;
                break;        
                case "v12":
                    x = 12;
                break;        
                case "v13":
                    x = 13;
                break;        
                case "v14":
                    x = 14;
                break;        
                case "v15":
                    x = 15;
                break;        
                }
                //alert(x);
                r[pc] = 0xF0 + x;
                pc++;
                r[pc] = 0x1E;
                pc++;
            }
            else if (cm[3].indexOf("v") > -1)
            {
                var x = 0;
                switch (cm[2])
                {
                case "v0":
                    x = 0;
                break;        
                case "v1":
                    x = 1;
                break;        
                case "v2":
                    x = 2;
                break;        
                case "v3":
                    x = 3;
                break;        
                case "v4":
                    x = 4;
                break;        
                case "v5":
                    x = 5;
                break;
                case "v6":
                    y = 6;
                break;            
                case "v7":
                    x = 7;
                break;        
                case "v8":
                    x = 8;
                break;        
                case "v9":
                    x = 9;
                break;        
                case "v10":
                    x = 10;
                break;        
                case "v11":
                    x = 11;
                break;        
                case "v12":
                    x = 12;
                break;        
                case "v13":
                    x = 13;
                break;        
                case "v14":
                    x = 14;
                break;        
                case "v15":
                    x = 15;
                break;        
                }
                var y = 0;
                switch (cm[3])
                {
                case "v0":
                    y = 0;
                break;        
                case "v1":
                    y = 1;
                break;        
                case "v2":
                    y = 2; 
                break;        
                case "v3":
                    y = 3; 
                break;        
                case "v4":
                    y = 4; 
                break;        
                case "v5":
                    y = 5; 
                break;
                case "v6":
                    y = 6;
                break;            
                case "v7":
                    y = 7; 
                break;        
                case "v8":
                    y = 8; 
                break;        
                case "v9":
                    y = 9; 
                break;        
                case "v10":
                    y = 10; 
                break;        
                case "v11":
                    y = 11; 
                break;        
                case "v12":
                    y = 12; 
                break;        
                case "v13":
                    y = 13; 
                break;        
                case "v14":
                    y = 14; 
                break;        
                case "v15":
                    y = 15; 
                break;       
                }            
                r[pc] = 0x80 + x;
                pc++;
                r[pc] = y * 16 + 4;
                pc++;
            }
            else
            {
                var x = 0;
                switch (cm[2])
                {
                case "v0":
                    x = 0;
                break;        
                case "v1":
                    x = 1;
                break;        
                case "v2":
                    x = 2; 
                break;        
                case "v3":
                    x = 3; 
                break;        
                case "v4":
                    x = 4; 
                break;        
                case "v5":
                    x = 5; 
                break;
                case "v6":
                    y = 6;
                break;            
                case "v7":
                    x = 7;
                break;        
                case "v8":
                    x = 8; 
                break;        
                case "v9":
                    x = 9; 
                break;        
                case "v10":
                    x = 10; 
                break;        
                case "v11":
                    x = 11; 
                break;        
                case "v12":
                    x = 12; 
                break;        
                case "v13":
                    x = 13; 
                break;        
                case "v14":
                    x = 14; 
                break;        
                case "v15":
                    x = 15; 
                break;        
                }
                r[pc] = 0x70 + x;
                pc++;
                r[pc] = cm[3];
                pc++;
            }
        break;
        case "load":
            if (cm[2] == "I")
            {
                r[pc] = 0xA0 + Math.floor(cm[3] / 256);
                pc++;
                r[pc] = cm[3] % 256;
                pc++;
            }
            else
            {
                var x = 0;
                switch (cm[2])
                {
                case "v0":
                    x = 0;
                break;        
                case "v1":
                    x = 1;
                break;        
                case "v2":
                    x = 2; 
                break;        
                case "v3":
                    x = 3; 
                break;        
                case "v4":
                    x = 4;
                break;        
                case "v5":
                    x = 5; 
                break;
                case "v6":
                    y = 6;
                break;           
                case "v7":
                    x = 7; 
                break;        
                case "v8":
                    x = 8; 
                break;        
                case "v9":
                    x = 9; 
                break;        
                case "v10":
                    x = 10; 
                break;        
                case "v11":
                    x = 11; 
                break;       
                case "v12":
                    x = 12; 
                break;        
                case "v13":
                    x = 13; 
                break;        
                case "v14":
                    x = 14; 
                break;        
                case "v15":
                    x = 15; 
                break;        
                }
                r[pc] = 0x60 + x;
                pc++;
                r[pc] = Number(cm[3]);
                pc++;
            }
        break;
        }
    }
    for (var a in la) 
    {

        for (i = 0; i < pc; i++)
        {
            if (r[i] == a)
            {

                c3 = la[a] - i - 4;
                r[i] = c3;
            }
        }
    }
    for (var i = 0; i < r.length; i++)
    {
        vm.memory[vm.PC_START + i] = r[i];
    }
    loop();
}
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) {
    if (key === 0x31) this.keypad[0x01] = n;
    if (key === 0x32) this.keypad[0x02] = n;
    if (key === 0x33) this.keypad[0x03] = n;
    if (key === 0x34) this.keypad[0x0c] = n;
    if (key === 0x51) this.keypad[0x04] = n;
    if (key === 0x57) this.keypad[0x05] = n;
    if (key === 0x45) this.keypad[0x06] = n;
    if (key === 0x52) this.keypad[0x0d] = n;
    if (key === 0x41) this.keypad[0x07] = n;
    if (key === 0x53) this.keypad[0x08] = n;
    if (key === 0x44) this.keypad[0x09] = n;
    if (key === 0x46) this.keypad[0x0e] = n;
    if (key === 0x5a) this.keypad[0x0a] = n;
    if (key === 0x58) this.keypad[0x00] = n;
    if (key === 0x43) this.keypad[0x0b] = n;
    if (key === 0x56) this.keypad[0x0f] = 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;
        case 0x0000:
            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);
onkeydown = (e) => vm.inputKey(e.keyCode, 1);
onkeyup = (e) => vm.inputKey(e.keyCode, 0);



成果物

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0