概要
2つのアセンブラプログラムが戦うゲームを見つけたので魔改造してみた。
参考にしたページ
cpuの仕様
レジスタ 3個8bit
Acc アキュムレータ
adr アドレスレジスタ
pc プログラムカウンタ
命令セット 14個12ビット
オペコード4ビット、値8ビット
メモリー 256個12ビット
ニーモニック | 意味 | コード |
---|---|---|
hlt | pc <- pc | 000 |
ld 12 | acc <- 12 | 10c |
ldr | acc <- adr | 200 |
str | adr <- acc | 300 |
ldm | acc <- [adr] | 400 |
stm | [adr] <- acc | 500 |
add 12 | acc <- acc + 12 | 60c |
sub 12 | acc <- acc - 12 | 70c |
gt 12 | (acc < 12) ? acc <- 1 : acc <- 0 | 80c |
if 12 | (acc = 1) ? pc <- 12 : pc <- pc + 1 | 90c |
jmp 12 | pc <- 12 | a0c |
api | alert | b00 |
ゲームの仕様
メモリー上に、プログラムを配置して、起動し、相手のプログラムを破壊した方が、勝ち。
写真
ブルーのプログラム
90番地に配置、0番地から50番地を2で埋める攻撃。
ld 2
stm
ldr
add 1
str
gt 50
if 90
api
hlt
レッドのプログラム
60番地に配置、80番地から100番地を3で埋める攻撃。
ld 80
str
ld 3
stm
ldr
add 1
str
gt 100
if 62
api
hlt
サンプルコード
const hex3 = (opcode) => ('000' + opcode.toString(16)).slice(-3);
let memory = Array(256).fill(0);
let programBlue = {
name: "プログラムブルー",
adr: 0,
acc: 0,
pc: 90,
code: [
"ld 2",
"stm",
"ldr",
"add 1",
"str",
"gt 50",
"if 90",
"api",
"hlt"
]
};
let programRed = {
name: "プログラムレッド",
adr: 0,
acc: 0,
pc: 60,
code: [
"ld 80",
"str",
"ld 3",
"stm",
"ldr",
"add 1",
"str",
"gt 100",
"if 62",
"api",
"hlt"
]
};
function updateMemoryDisplay() {
var h = '<table>';
for (i = 0; i < 256; i = i + 16)
{
h += '<tr>';
h += '<td>' + hex3(memory[i + 0]) + '<\/td>';
h += '<td>' + hex3(memory[i + 1]) + '<\/td>';
h += '<td>' + hex3(memory[i + 2]) + '<\/td>';
h += '<td>' + hex3(memory[i + 3]) + '<\/td>';
h += '<td>' + hex3(memory[i + 4]) + '<\/td>';
h += '<td>' + hex3(memory[i + 5]) + '<\/td>';
h += '<td>' + hex3(memory[i + 6]) + '<\/td>';
h += '<td>' + hex3(memory[i + 7]) + '<\/td>';
h += '<td>' + hex3(memory[i + 8]) + '<\/td>';
h += '<td>' + hex3(memory[i + 9]) + '<\/td>';
h += '<td>' + hex3(memory[i + 10]) + '<\/td>';
h += '<td>' + hex3(memory[i + 11]) + '<\/td>';
h += '<td>' + hex3(memory[i + 12]) + '<\/td>';
h += '<td>' + hex3(memory[i + 13]) + '<\/td>';
h += '<td>' + hex3(memory[i + 14]) + '<\/td>';
h += '<td>' + hex3(memory[i + 15]) + '<\/td>';
h += '<\/tr>';
}
h += '<\/table>';
document.getElementById('memory').innerHTML = h;
}
function updateProgramDisplay() {
document.getElementById("blueStatus").innerHTML = `
現在位置: ${programBlue.pc}, acc: ${programBlue.acc}, adr: ${programBlue.adr}
`;
document.getElementById("redStatus").innerHTML = `
現在位置: ${programRed.pc}, acc: ${programRed.acc}, adr: ${programRed.adr}
`;
document.getElementById("blueInstructions").innerText = programBlue.code.join("\n");
document.getElementById("redInstructions").innerText = programRed.code.join("\n");
}
function executeInstruction(program) {
let instruction = memory[program.pc];
let opcode = instruction & 0xf00;
let opland = instruction & 0xff;
switch (opcode)
{
case 0: //"hlt":
program.pc--;
break;
case 0x100: //"ld":
program.acc = opland;
break;
case 0x200: //"ldr":
program.acc = program.adr;
break;
case 0x300: //"str":
program.adr = program.acc;
break;
case 0x400: //"ldm":
program.acc = memory[program.adr];
break;
case 0x500: //"stm":
memory[program.adr] = program.acc;
break;
case 0x600: //"add":
program.acc += opland;
break;
case 0x700: //"sub":
program.acc -= opland;
break;
case 0x800: //"gt":
if (program.acc < opland)
{
program.acc = 1;
}
else
{
program.acc = 0;
}
break;
case 0x900: //"if":
if (program.acc == 1)
{
program.pc = opland - 1;
}
break;
case 0xA00: //"jmp":
program.pc = opland - 1;
break;
case 0xB00: //"api":
alert(`${program.name} がゴールしました!`);
break;
}
program.pc++;
}
function setmem(program) {
let opcode;
let adr = program.pc;
for (i = 0; i < program.code.length; i++)
{
let instruction = program.code[i];
let parts = instruction.split(" ");
let opland = parseInt(parts[1]) & 0xff;
switch (parts[0])
{
case "hlt":
opcode = 0;
memory[adr] = opcode;
break;
case "ld":
opcode = 1 << 8 | opland;
memory[adr] = opcode;
break;
case "ldr":
opcode = 2 << 8;
memory[adr] = opcode;
break;
case "str":
opcode = 3 << 8;
memory[adr] = opcode;
break;
case "ldm":
opcode = 4 << 8;
memory[adr] = opcode;
break;
case "stm":
opcode = 5 << 8;
memory[adr] = opcode;
break;
case "add":
opcode = 6 << 8 | opland;
memory[adr] = opcode;
break;
case "sub":
opcode = 7 << 8 | opland;
memory[adr] = opcode;
break;
case "gt":
opcode = 8 << 8 | opland;
memory[adr] = opcode;
break;
case "if":
opcode = 9 << 8 | opland;
memory[adr] = opcode;
break;
case "jmp":
opcode = 10 << 8 | opland;
memory[adr] = opcode;
break;
case "api":
opcode = 11 << 8;
memory[adr] = opcode;
break;
default:
alert(instruction);
}
adr++;
}
}
let battleInterval;
function runBattle() {
battleInterval = setInterval(() => {
executeInstruction(programBlue);
executeInstruction(programRed);
updateMemoryDisplay();
updateProgramDisplay();
}, 300);
}
function stopBattle() {
clearInterval(battleInterval);
alert(`終了しました!`);
}
setmem(programBlue);
setmem(programRed);
updateMemoryDisplay();
updateProgramDisplay();
成果物
対戦結果
ブルーのプログラムは、レッドに消された。
以上。