コンピュータが扱うのは0と1だけと繰り返し伝えてきました。
数字だけでどうやってコンピュータに指示するんだ?
この疑問は誰でも持つのです。
ここが理解できれば、コンピュータの頭脳(CPU)を理解することになります。
実は、数字の羅列の中には命令コードと呼ばれる数字と、その命令を実行するために
必要なデータとしての数字の2種類あるのです。
「命令+データ」が1つの組み合わせとなって、多数並べてプログラムとして動くのです。
(一部データが不要な命令もあります)
しかし、命令もデータも同じ数字ですので、一見区別できません。
また、数字がずれた場合、CPUはどのようにずれたか分かりませんので、無理やり実行しようとして暴走します。
ゲーム機が突然変な画面になって止まるのは、この様な理由からです。
CPUがプログラムを実行するためのルールは以下の通りです。
- 命令として数字を取り出す。
- 命令によって、処理に必要なデータの数が分かるので、その個数分の数字を取り出す。
- 命令を実行する。
- 1に戻って繰り返す。
たったこれだけです。
簡単なCPUを仮想的に作って処理させてみましょう。
シンプル1号というCPUを作ってみます。
このCPUは8ビット単位で処理します。
よって、命令の種類は最大で256個となります。
8ビットの数字を管理できる箱を1つ持っています。
計算はこの箱を使って行います。
この箱のことをアキュムレータ(以下ACC)と呼びます。
| 命令を表す数字(16進数) |
命令内容 |
必要なデータ数 |
| 00h |
ACCに、データの数字を代入する |
1 |
| 01h |
ACCに、データの数字を加える |
1 |
| FEh |
画面にACCの内容を表示する |
0 |
| FFh |
CPUの動作を停止する |
0 |
3種類の命令しか持っていません。
では、プログラムを作ってみます。
00h 14h 01h 3Fh FEh FFh
たった5つの数字です。
では、シンプル1号に処理させてみましょう。
まず、00hを取り出し、命令表と比較すると、命令番号00hが見つかります。
必要なデータ数は1つですので、次の数値14hを取り出します。
この数値をACCに代入し、この命令の処理は終わります。(ACC=14h)
次に、01hを取り出し、命令表と比較すると、滅入れ番号01hが見つかります。
必要なデータ数は1つですので、次の数値3Fhを取り出します。
この数値をACCに加え、この命令の処理は終わります。(ACC=14h+3Fh=53h)
FEhを取り出し、命令表と比較すると命令番号FEhが見つかります。
必要なデータ数は0なので、データは取り出さず命令を実行します。
画面に53hと表示し、この命令の処理は終わります。
FFhを取り出し、命令表と比較すると命令番号FFhが見つかります。
必要なデータ数は0なので、データは取り出さず命令を実行します。
CPUは動作を停止します。
以上でCPUの動作が完了します。
見事に8ビットの数字の羅列だけでプログラムが実行できましたね!
実際のCPUには数多くの命令がありますが、処理内容の基本はシンプル1号とまったく同じなのです。
テストとして、シンプル1号をJavaScriptで実現しました。
試してみてください。
<script type="text/javascript">
var acc = 0;
var cpuRunning = true;
var program = [];
var opcodes = {
0x00: {
comment: "ACCにデータの数字を代入します",
oplands: 1,
func: function(oplands) { acc=oplands.pop(); }
},
0x01: {
comment: "ACCにデータの数字を加えます",
oplands: 1,
func: function(oplands) { acc += oplands.pop(); }
},
0xFE: {
comment: "ACCの内容を画面に表示します",
oplands: 0,
func: function(oplands) { return "アキュムレータの内容は"+acc+"です"; }
},
0xFF: {
comment: "CPUの処理を停止します",
oplands: 0,
func: function(oplands) { cpuRunning = false; }
}
};
function SimpleNo1()
{
var getData = function()
{
if(ip < 0 || ip >= program.length) {
throw Error("CPUはプログラムの範囲外に飛び出し、暴走しました(異常終了)");
}
return program[ip++];
};
var toHex = function( n )
{
var str16 = "0123456789ABCDEF";
n = Number(n);
var hi = Math.floor(n / 16);
var lo = n % 16;
return str16.substr(hi, 1)+str16.substr(lo, 1);
}
program = [];
document.cpu.output.value = "プログラムコード投入\n";
var prg = new String(document.cpu.program.value);
prg = prg.replace(/^\s+|\s+$/g, "");
var tokens = prg.split(/\s+/);
for(var i = 0; i < tokens.length; i++) {
var hexCode = tokens[i];
if(!hexCode.match(/^[0-9A-Fa-f]{2}/)) {
document.cpu.output.value += "プログラムコード異常("+hexCode+") 異常終了\n";
return;
}
program.push(parseInt(hexCode, 16));
}
document.cpu.output.value += "CPU起動\n";
var ip = 0;
cpuRunning = true;
document.cpu.output.value += "CPU初期化完了\n";
try {
while(cpuRunning) {
var opcode = getData();
document.cpu.output.value += "命令コードを取り出し("+toHex(opcode)+")\n";
var op = opcodes[opcode];
if(op == null) {
throw new Error("命令表にない命令(コード"+toHex(opcode)+")を実行しようとし、暴走しました(異常終了)");
}
var opl = [];
document.cpu.output.value += op.comment + "\n";
document.cpu.output.value += "必要データ数は"+op.oplands+"バイトです\n";
for(var i = 0; i < op.oplands; i++) {
var d = getData();
document.cpu.output.value += "データを取り出し("+toHex(d)+")\n";
opl.push(d);
}
document.cpu.output.value += "実行します\n";
var output = op.func(opl);
if(output != null) {
document.cpu.output.value += "実行結果:"+output+"\n";
}
}
document.cpu.output.value += "正常終了!\n";
}
catch(e) {
document.cpu.output.value += e.message+"\n";
}
}
</script>
<div style="border: 1px solid black;">
<form name="cpu" id="cpu">
プログラム:<input type="text" size="50" name="program" value="00 14 01 3F FE FF"/><br/>
※16進数を1バイトずつスペースで区切って入力してください。<br/>
<input type="button" value="実行" onclick="SimpleNo1();"/><br/>
<hr/>
実行結果<br/>
<textarea name="output" cols="50" rows="10"></textarea></br/>
</form>
</div>
admin プログラミング講座 2進数