Skip to content

Instantly share code, notes, and snippets.

@sugarflower
Created November 30, 2023 13:07
Show Gist options
  • Save sugarflower/5d73400a437359a186bc30bb2f76679b to your computer and use it in GitHub Desktop.
Save sugarflower/5d73400a437359a186bc30bb2f76679b to your computer and use it in GitHub Desktop.

性懲りもなく幾度目か

もう一度しっかり作ろうってことで。

MEMORY_SIZE = 0xffff

class Memory:
    def __init__(self):
        self.memory = bytearray([0] * MEMORY_SIZE)

    def get(self, addr):
        return self.memory[addr]

    def set(self, addr, value):
        self.memory[addr] = value & 0xff

    def get_word(self, addr):
        return self.get(addr) | self.get(addr + 1) << 8

    def set_word(self, addr, value):
        self.set(addr, (value & 0xff))
        self.set(addr + 1, (value >> 8))
class Register:
    def __init__(self, memory):
        # B C D E H L (M) A F SP PC
        self.register = bytearray([0] * 13)
        self.memory = memory

    def get(self, index):
        return self.register[index]

    def set(self, index, value):
        self.register[index] = value & 0xff

        if index == 6:
            self.memory.set(self.get_word(2), value)
        if index == 4 or index == 5:
            self.update()

    def get_word(self, index):
        # BC DE HL AF SP PC
        if index < 3:
            hval = self.get(index<<1)
            lval = self.get((index<<1)+1)
        else:
            hval = self.get((index<<1)+1)
            lval = self.get((index<<1)+2)
        return lval << 8 | hval

    def set_word(self, index, value):
        # BC DE HL AF SP PC
        hval = value & 0xff
        lval = value >> 8
        if index < 3:
            self.set(index<<1, hval)
            self.set((index<<1)+1, lval)
        else:
            self.set((index<<1)+1, hval)
            self.set((index<<1)+2, lval)

    def update(self):
        self.register[6] = self.memory.get(self.get_word(2))

    def monitor(self):
        print("B %02x  C %02x" % (self.register[0], self.register[1]))
        print("D %02x  E %02x" % (self.register[2], self.register[3]))
        print("H %02x  L %02x" % (self.register[4], self.register[5]))
        print("M %02x  " % (self.register[6],))
        print("A %02x  F %02x" % (self.register[7], self.register[8]))
        print("SP %04x" % self.get_word(4))
        print("PC %04x" % self.get_word(5))

勢いでここまで書いた。 レジスタクラスにメモリクラスのインスタンスを渡すようにしてあるけれどどうなんだこれとちょっと思う。

if __name__ == "__main__":
    m = Memory()
    r = Register(m)

    r.set_word(0, 0x1122)
    r.set_word(1, 0x3344)
    r.set_word(2, 0x5566)
    r.set_word(3, 0x7788)
    r.set_word(4, 0x99aa)
    r.set_word(5, 0xbbcc)
    r.monitor()

ひとまずのテストコード。 今日はここまで。

@sugarflower
Copy link
Author

sugarflower commented Dec 2, 2023

CPUクラスに着手。混乱しがちな fetch と push/pop を実装
それとpythonでは問題になる8bit、16bitの値の補正用の関数としてa16とa8を実装

def a16(value):
    if value > MEMORY_SIZE:
        value -= MEMORY_SIZE + 1
    elif value < 0:
        value += MEMORY_SIZE
    return value & 0xffff

def a8(value):
    if value > 0xff:
        value -= 0xff + 1
    elif value < 0:
        value += 0xff
    return value & 0xff


class CPU:
    def __init__(self, memory, register):
        self.memory = memory
        self.register = register

    def fetch(self):
        pc = self.register.get_word(5)
        value = self.memory.get(pc)
        self.register.set_word(5, a16(pc + 1))
        return value

    def fetch_word(self):
        pc = self.register.get_word(5)
        value = self.memory.get_word(pc)
        self.register.set_word(5, a16(pc + 2))
        return value

    def push(self, value):
        sp = self.register.get_word(4) - 2
        self.memory.set_word(sp, value)
        self.register.set_word(4, a16(sp))

    def pop(self):
        sp = self.register.get_word(4)
        value = self.memory.get_word(sp)
        self.register.set_word(4, a16(sp + 2))
        return value

    def mainloop(self):
        pass
        # メインループはここ

CPUクラスの初期化にはMemoryとRegisterそれぞれのインスタンスを必要とするので以下の様に実行する

if __name__ == "__main__":
    memory = Memory()
    register = Register(memory)
    cpu = CPU(memory=memory, register=register)

   # ブートストラップとかROM処理とかはこの辺りに入れれば良さそう。
    
    cpu.mainloop()

@sugarflower
Copy link
Author

fix

class Memory:
    def __init__(self):
        self.memory = bytearray([0] * (MEMORY_SIZE + 1))
def a16(value):
    if value > MEMORY_SIZE:
        value -= MEMORY_SIZE + 1
    elif value < 0:
        value += MEMORY_SIZE
    return value & MEMORY_SIZE

油断をするとすぐバグが入り込む。

mainloop

CPUクラスのmainloopについて。
デコーダーの基本構造と、とりあえずのMOVを実装する。
0x67のときだけはHLTなのでそこだけ注意。HLTの扱いについては後程考える。

    def mainloop(self):
        while self.running:
            opcode = self.fetch()
            op1 = opcode >> 6


            if op1 == 0:
                pass
            
            elif op1 == 1: # MOV / HLT
                if opcode == 0x67: # HLT
                    self.running = False
                    break
                else: # MOV
                    op2 = opcode >> 3 & 0x07
                    op3 = opcode & 0x07
                    self.register.set(op2, self.register.get(op3))

            elif op1 == 2: # ADD/ ADC / SUB / SBB / ANA / XRA / ORA / CMP
                pass

            elif op1 == 3:
                pass

            self.register.update()
            time.sleep(0.001)
            self.running = False

time.sleepは入れておかないとPCがファン全開でうなり始めるので入れておいた。

if __name__ == "__main__":
    memory = Memory()
    register = Register(memory)
    cpu = CPU(memory=memory, register=register)

    register.set(1, 0xaa) # C <- 0xAA

    memory.set(0, 0x79) # MOV A, C
    memory.set(1, 0x67) # HLT

    register.monitor()
    
    
    cpu.mainloop()

このようにして動作を確かめられる。

ところでコードをPythonで書いているのは、パッと見て理解しやすくテストがしやすいからなので別にPythonで実装する必要はないと思う、むしろRustとかC++で書いた方が速いので実際に使う場合はそちらを推奨します。

@sugarflower
Copy link
Author

sugarflower commented Dec 5, 2023

いろいろ修正してるけど言及しきれない。

残るは op1==3 のブロック

MEMORY_SIZE = 0xffff
#MEMORY_SIZE = 0x0f
import time

class Memory:
    def __init__(self):
        self.memory = bytearray([0] * (MEMORY_SIZE + 1))

    def get(self, addr):
        return self.memory[addr]

    def set(self, addr, value):
        self.memory[addr] = value & 0xff

    def get_word(self, addr):
        return self.get(addr) | self.get(addr + 1) << 8

    def set_word(self, addr, value):
        self.set(addr, (value & 0xff))
        self.set(addr + 1, (value >> 8))

class Register:
    def __init__(self, memory):
        # B C D E H L (M) A F SP PC
        self.register = bytearray([0] * 13)
        self.memory = memory

    def get(self, index):
        if index == 8:
            buf = self.register[8]
            buf = buf & 0b1101_0111
            buf = buf | 0b0000_0010
        else:
            buf = self.register[index]
        return buf

    def set(self, index, value):
        self.register[index] = value & 0xff

        if index == 6:
            self.memory.set(self.get_word(2), value)
        if index == 4 or index == 5:
            self.update()

    def get_word(self, index):
        # BC DE HL AF SP PC
        if index < 3:
            lval = self.get(index << 1)
            hval = self.get((index << 1) + 1)
        else:
            lval = self.get((index << 1) + 1)
            hval = self.get((index << 1) + 2)
        return (lval << 8) | hval

    def set_word(self, index, value):
        # BC DE HL AF SP PC
        hval = value & 0xff
        lval = value >> 8
        if index < 3:
            self.set(index << 1, lval)
            self.set((index <<1 ) + 1, hval)
        else:
            self.set((index << 1) + 1, lval)
            self.set((index << 1) + 2, hval)

    def update(self):
        self.register[6] = self.memory.get(self.get_word(2))

    def monitor(self):
        print("B %02X  C %02X" % (self.get(0), self.get(1)) )
        print("D %02X  E %02X" % (self.get(2), self.get(3)) )
        print("H %02X  L %02X" % (self.get(4), self.get(5)) )
        print("M %02X    SZ0A0P1C" % self.get(6) )
        print("A %02X  F %s" % (self.get(7), format(self.get(8), "08b")) )
        print("SP %04X" % self.get_word(4) )
        print("PC %04X" % self.get_word(5) )
        print("")



def a16(value):
    if value > MEMORY_SIZE:
        value -= MEMORY_SIZE + 1
    elif value < 0:
        value += MEMORY_SIZE
    return value & MEMORY_SIZE

def a8(value):
    if value > 0xff:
        value -= 0xff + 1
    elif value < 0:
        value += 0xff
    return value & 0xff
class CPU:
    def __init__(self, memory, register):
        self.memory = memory
        self.register = register
        self.running = True

    def fetch(self):
        pc = self.register.get_word(5)
        value = self.memory.get(pc)
        self.register.set_word(5, a16(pc + 1))
        return value

    def fetch_word(self):
        pc = self.register.get_word(5)
        value = self.memory.get_word(pc)
        self.register.set_word(5, a16(pc + 2))
        return value

    def push(self, value):
        sp = self.register.get_word(4) - 2
        self.memory.set_word(sp, value)
        self.register.set_word(4, a16(sp))

    def pop(self):
        sp = self.register.get_word(4)
        value = self.memory.get_word(sp)
        self.register.set_word(4, a16(sp + 2))
        return value


    # flg
    # S Z 0 A 0 P 1 C
    # A (HC) not impl
    def check_parity(self, buf):
        v = buf
        v ^= v >> 4
        v ^= v >> 2
        v ^= v >> 1
        v = v & 1
        f = self.register.get(8)
        self.register.set(8, f & 0b11111011 | (v << 2))

    def check_carry(self, buf):
        v = 0
        if buf > 0xff:
            v = 1
        f = self.register.get(8)
        self.register.set(8, f & 0b11111110 | v)

    # この処理ではローテ―ト命令でボローを判断出来ないので注意
    def check_signed(self, buf):
        v = 0
        if buf < 0 or buf & 0x100 != 0:
            v = 1
        f = self.register.get(8)
        self.register.set(8, f & 0b01111111 | (v << 7))

    def check_zero(self, buf):
        v = 0
        if buf == 0:
            v = 1
        f = self.register.get(8)
        self.register.set(8, f & 0b10111111 | (v << 6))


    def get_parity(self):
        f = self.register.get(8)
        return (f >> 2) & 1

    def get_carry(self):
        f = self.register.get(8)
        return f & 1

    def get_signed(self):
        f = self.register.get(8)
        return (f >> 7) & 1

    def get_zero(self):
        f = self.register.get(8)
        return (f >> 6) & 1


    def set_carry(self, v):
        self.register.set(8, f & 0b11111110 | v)

    def set_signed(self, v):
        self.register.set(8, f & 0b01111111 | (v << 7))


    def mainloop(self):
        while self.running:
            opcode = self.fetch()
            op1 = opcode >> 6
            op2 = opcode >> 3 & 0x07
            op3 = opcode & 0x07
            
            if op1 == 0:
                if op3 == 0: #NOP 保留
                    pass
                
                elif op3 == 1: #LXI DAD
                    op4 = op2 & 1
                    op2 = op2 >> 1
                    if op2 == 3: # AF指定の場合SPに修正
                        op2 = 4
                    if op4 == 0: #LXI
                        v = self.fetch_word()
                        self.register.set_word(op2, v)
                    else: #DAD
                        hl = self.register.get_word(2)
                        tgt = self.register.get_word(op2)
                        self.register.set_word(2, a16(hl + tgt))
                        
                elif op3 == 2: #STAX SHLD STA LDAX LHLD LDA
                    if op2 in (0, 2): #STAX
                        addr = self.register.get_word(op2 >> 1)
                        a = self.register.get(7)
                        self.memory.set(addr, a)
                    elif op2 == 4: #SHLD
                        hl = self.register.get_word(2)
                        addr = self.fetch_word()
                        self.memory.set_word(addr, hl)
                    elif op2 == 6: #STA
                        a = self.register.get(7)
                        addr = self.fetch_word()
                        self.memory.set(addr, a)
                    elif op2 in (1, 3): #LDAX
                        addr = self.register.get_word((op2 - 1) >> 1)
                        v = self.memory.get(addr)
                        self.register.set(7, v)
                    elif op2 == 5: #LHLD
                        addr = self.fetch_word()
                        v = self.memory.get_word(addr)
                        self.register.set_word(2, v)
                    elif op2 == 7: #LDA
                        addr = self.fetch_word()
                        v = self.memory.get(addr)
                        self.register.set(7, v)

                elif op3 == 3: #INX DCX
                    op4 = op2 & 1
                    op2 = op2 >> 1
                    if op4 == 0: #INX
                        v = self.register.get_word(op2) + 1
                        self.register.set_word(op2, a16(v))
                    else: #DCX
                        v = self.register.get_word(op2) - 1
                        self.register.set_word(op2, a16(v))
                        
                elif op3 == 4: #INR
                    v = self.register.get(op2) + 1
                    self.check_parity(v)
                    self.check_signed(v)
                    self.check_zero(v)
                    self.register.set(op2, a8(v))
                    
                elif op3 == 5: #DCR
                    v = self.register.get(op2) - 1
                    self.check_parity(v)
                    self.check_signed(v)
                    self.check_zero(v)
                    self.register.set(op2, a8(v))
                    
                elif op3 == 6: #MVI
                    v = self.fetch()
                    self.register.set(op2, v)
                    
                elif op3 == 7: # RLC RAL DAA STC RRC RAR CMA CMC
                    if op2 == 0: #RLC
                        a = self.register.get(7)
                        c = a >> 7 & 1
                        a = (a << 1) & 0xff | c
                        self.set_carry(c)
                        self.register.set(7, a)

                    elif op2 == 1: #RRC
                        a = self.register.get(7)
                        c = a & 1
                        a = (a >> 1) | (c << 7)
                        self.set_carry(c)
                        self.register.set(7, a)
                        
                    elif op2 == 2: #RAL
                        a = self.register.get(7)
                        b = a >> 7 & 1
                        c = self.get_carry()
                        a = (a << 1) & 0xff | c
                        self.register.set(7, a)
                        self.set_carry(b)

                    elif op2 == 3: #RAR
                        a = self.register.get(7)
                        b = a & 1
                        c = self.get_carry()
                        a = (a >> 1) | (c << 7)
                        self.register.set(7, a)
                        self.set_carry(b)

                    elif op2 == 4: #DAA
                        pass
                    
                    elif op2 == 5: #CMA
                        a = self.register.get(7) ^ 0xff
                        self.register.set(7, a)

                    elif op2 == 6: #STC
                        self.set_carry(1)
                        
                    elif op2 == 7: #CMC
                        c = self.get_carry()
                        self.set_carry(c ^ 1)

            
            elif op1 == 1: # MOV / HLT
                if opcode == 0x67: # HLT
                    self.running = False
                    break
                else: # MOV
                    self.register.set(op2, self.register.get(op3))


            elif op1 == 2: # ADD/ ADC / SUB / SBB / ANA / XRA / ORA / CMP
                buf = 0
                acm = self.register.get(7)
                tgt = self.register.get(op3)
                if op2 == 0: # ADD
                    buf = acm + tgt
                elif op2 == 1: # ADC
                    c = self.get_carry()
                    buf = acm + tgt + c
                elif op2 == 2: # SUB
                    buf = acm - tgt
                elif op2 == 3: # SBB
                    c = self.get_carry()
                    buf = acm - tgt - c
                elif op2 == 4: # ANA
                    buf = acm & tgt
                elif op2 == 5: # XRA
                    buf = acm ^ tgt
                elif op2 == 6: # ORA
                    buf = acm | tgt
                elif op2 == 7: # CMP
                    buf = acm - tgt

                # フラグ処理 Aフラグは未実装
                self.check_parity(buf)
                self.check_signed(buf)
                self.check_zero(buf)

                # ADC SBBはキャリーに変化を起こさない
                if op2 not in (1, 3):
                    self.check_carry(buf)

                # CMPはアキュムレータに変化を起こさない
                if op2 != 7:
                    self.register.set(7, buf)


            elif op1 == 3:
                pass



            self.register.update()
            time.sleep(0.001)

            self.register.monitor()
            
        

if __name__ == "__main__":
    memory = Memory()
    register = Register(memory)
    cpu = CPU(memory=memory, register=register)

    #register.set_word(2, 0x1234)
    memory.set(0, 0x2a)
    memory.set(1, 0x04)
    memory.set(2, 0x00)
    memory.set(3, 0x67)
    memory.set(4, 0xaa)
    memory.set(5, 0x55)
    
    register.monitor()

    cpu.mainloop()

    for i in range(10):
        print(format(memory.get(i), "02x"), end=" ")
    print()

これでほぼ3/4実装完了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment