0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで作る8ビットCPUエミュレータ入門

Posted at

~ALU・バス・I/O・命令セットを一から構築~

このプロジェクトでは、Pythonを使ってシンプルな8ビットCPUのエミュレータを実装します。ALU、バス、I/Oデバイス、命令セットなど、CPUを構成する各要素を自作し、最終的には簡単なプログラムを実行させます。


構成概要

このエミュレータは以下の構成要素で構成されています:

  • ALU(算術論理演算器):加算、減算、論理演算などを処理
  • バス(Bus):レジスタやI/Oとのデータのやりとり
  • レジスタ(Register):汎用レジスタAとB
  • I/Oデバイス(IODevice):出力処理
  • CPU(中央処理装置):命令の実行・管理

Pythonコード

# 8-bit CPU Emulator with ALU, Bus, I/O, and Instruction Set
# 8ビットCPUエミュレータ(ALU、バス、I/O、命令セットを含む)

class ALU:
    def __init__(self):
        self.result = 0          # 演算結果 / Result of ALU operation
        self.zero_flag = False  # ゼロフラグ / Zero flag

    def operate(self, op, a, b):
        # 各種演算を実行 / Perform operations based on opcode
        if op == "ADD":
            self.result = (a + b) & 0xFF  # 8ビット加算 / 8-bit addition
        elif op == "SUB":
            self.result = (a - b) & 0xFF  # 8ビット減算 / 8-bit subtraction
        elif op == "AND":
            self.result = a & b           # 論理積 / Bitwise AND
        elif op == "OR":
            self.result = a | b           # 論理和 / Bitwise OR
        elif op == "MUL":
            self.result = (a * b) & 0xFF  # 8ビット乗算 / 8-bit multiplication
        elif op == "CMP":
            self.result = 0
            self.zero_flag = (a == b)    # 比較結果(等しいならZF=1)/ Set zero flag if equal
            return None
        self.zero_flag = (self.result == 0)  # 結果が0ならZF=1 / Update zero flag
        return self.result

    def get_zero_flag(self):
        return self.zero_flag  # ZFを返す / Return zero flag


class Bus:
    def __init__(self):
        self.data = 0  # バス上のデータ / Data on the bus

    def write(self, value):
        self.data = value & 0xFF  # 8ビットに制限 / Limit to 8-bit

    def read(self):
        return self.data  # データ読み出し / Read data


class IODevice:
    def __init__(self):
        self.output = []  # 出力データの履歴 / History of outputs

    def write(self, value):
        self.output.append(value)
        print(f"[I/O] Output Device Received: {value}")  # 出力表示 / Show output


class Register:
    def __init__(self):
        self.value = 0  # レジスタの値 / Value of the register

    def load(self, val):
        self.value = val & 0xFF  # 8ビット制限 / Limit to 8-bit

    def read(self):
        return self.value  # 値の読み出し / Read value


class CPU:
    def __init__(self):
        # レジスタ、PC、メモリなどの初期化 / Initialize registers, PC, memory, etc.
        self.regA = Register()
        self.regB = Register()
        self.PC = 0
        self.memory = [0] * 256  # 256バイトメモリ / 256-byte memory
        self.alu = ALU()
        self.bus = Bus()
        self.io = IODevice()
        self.halt = False

    def load_program(self, program):
        self.memory[:len(program)] = program  # プログラムのロード / Load program into memory

    def fetch(self):
        instr = self.memory[self.PC]  # 命令の取得 / Fetch instruction
        self.PC += 1
        return instr

    def execute(self, instr):
        # 命令のデコードと実行 / Decode and execute instruction
        if instr == 0x01:  # LOAD A, imm
            val = self.fetch()
            self.regA.load(val)
        elif instr == 0x02:  # LOAD B, imm
            val = self.fetch()
            self.regB.load(val)
        elif instr == 0x03:  # ADD A, B
            result = self.alu.operate("ADD", self.regA.read(), self.regB.read())
            self.regA.load(result)
        elif instr == 0x04:  # MUL A, B
            result = self.alu.operate("MUL", self.regA.read(), self.regB.read())
            self.regA.load(result)
        elif instr == 0x05:  # CMP A, B
            self.alu.operate("CMP", self.regA.read(), self.regB.read())
        elif instr == 0x06:  # OUT A
            self.io.write(self.regA.read())
        elif instr == 0x07:  # MOV A to Bus
            self.bus.write(self.regA.read())
        elif instr == 0x08:  # MOV Bus to B
            self.regB.load(self.bus.read())
        elif instr == 0x09:  # SUB A, B
            result = self.alu.operate("SUB", self.regA.read(), self.regB.read())
            self.regA.load(result)
        elif instr == 0x0A:  # AND A, B
            result = self.alu.operate("AND", self.regA.read(), self.regB.read())
            self.regA.load(result)
        elif instr == 0x0B:  # OR A, B
            result = self.alu.operate("OR", self.regA.read(), self.regB.read())
            self.regA.load(result)
        elif instr == 0xFF:  # HALT
            self.halt = True  # プログラム終了 / Stop the program

        # 現在の状態を表示 / Display current CPU state
        print(f"PC: {self.PC-1}, A: {self.regA.read()}, B: {self.regB.read()}, Bus: {self.bus.read()}, ZF: {self.alu.get_zero_flag()}")

    def run(self):
        # 命令を連続的に実行 / Run until HALT instruction
        while not self.halt:
            instr = self.fetch()
            self.execute(instr)


# サンプルプログラム:A=7, B=3, A=A*B → Aをバス経由でBにコピー → Aを出力 → 比較 → AND → OR → 停止
# Sample Program: A=7, B=3, A=A*B → Copy A to B via Bus → Output A → Compare → AND → OR → HALT
program = [
    0x01, 0x07,  # LOAD A, 7
    0x02, 0x03,  # LOAD B, 3
    0x04,        # MUL A, B
    0x07,        # MOV A to Bus
    0x08,        # MOV Bus to B
    0x06,        # OUT A
    0x05,        # CMP A, B
    0x0A,        # AND A, B
    0x0B,        # OR A, B
    0xFF         # HALT
]

# CPU実行 / Execute CPU
cpu = CPU()
cpu.load_program(program)
cpu.run()

結果
image.png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?