コンテンツにスキップ

023: 「Accumulatorは一つだけ」の解答

問題 / ヒント

難易度: ☆☆

方針

まず、現在のPCが指す命令を読み、命令名とoperandに分けます。 next_pcpc + 1 として用意しておき、分岐命令だけ必要に応じて上書きします。 Jump 0 の場合は haltedTrue にします。

実装

def make_machine(program=None, inputs=None):
    memory = [0] * 32
    if program is not None:
        for address, value in enumerate(program):
            memory[address] = value
    if inputs is None:
        inputs = []
    return {
        "memory": memory,
        "pc": 0,
        "acc": 0,
        "inputs": list(inputs),
        "outputs": [],
        "halted": False,
    }


def step(machine):
    if machine["halted"]:
        return

    pc = machine["pc"]
    instruction = machine["memory"][pc]
    name, operand = decode_instruction(instruction)
    next_pc = pc + 1

    if name == "Jump":
        if operand == 0:
            machine["halted"] = True
            return
        if machine["acc"] > 0:
            next_pc = operand
    elif name == "Add":
        machine["acc"] += machine["memory"][operand]
    elif name == "Sub":
        machine["acc"] -= machine["memory"][operand]
    elif name == "Load":
        machine["acc"] = machine["memory"][operand]
    elif name == "Store":
        machine["memory"][operand] = machine["acc"]
    elif name == "Read":
        if machine["inputs"] == []:
            raise EOFError("input is empty")
        machine["memory"][operand] = machine["inputs"].pop(0)
    elif name == "Write":
        machine["outputs"].append(machine["memory"][operand])
    elif name == "Shift":
        machine["acc"] <<= operand

    machine["pc"] = next_pc

確認

machine = make_machine([
    assemble_line("Load 10"),
    assemble_line("Add 11"),
    assemble_line("Store 12"),
    assemble_line("Stop"),
])
machine["memory"][10] = 7
machine["memory"][11] = 5
step(machine)
assert machine["acc"] == 7
step(machine)
assert machine["acc"] == 12
step(machine)
assert machine["memory"][12] == 12

発展

PCが範囲外のまま step() を呼ぶと、Pythonのリストアクセスで IndexError が起こります。 エミュレータのエラーとして明示したい場合は、命令を読む前に 0 <= pc < 32 を確認します。