DI、EI、DAAは未実装、NOPは保留
FレジスタのAフラグは未実装
テスト不足のため結構クリティカルな問題がありそう。
IN/OUTで利用する為Hardwareクラスを追加。
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 d16(value):
if value > 0xffff:
value -= 0xffff + 1
elif value < 0:
value += 0xffff
return value & 0xffff
def d8(value):
if value > 0xff:
value -= 0xff + 1
elif value < 0:
value += 0xff
return value & 0xff
class CPU:
def __init__(self, memory, register, hardware):
self.memory = memory
self.register = register
self.hardware = hardware
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 = a16(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))
# group 0 - 3
# con 0 or 1
def check_flg(self, op):
f = op & 1
t = op >> 1
if t == 0: # Z
return self.get_zero() == f
elif t == 1: # C
return self.get_carry() == f
elif t == 2: # P
return self.get_parity() == f
elif t == 3: # S
return self.get_signed() == f
def calc(self, op, v):
buf = 0
acm = self.register.get(7)
if op == 0: # ADD
buf = acm + v
elif op == 1: # ADC
c = self.get_carry()
buf = acm + v + c
elif op == 2: # SUB
buf = acm - v
elif op == 3: # SBB
c = self.get_carry()
buf = acm - v - c
elif op == 4: # ANA
buf = acm & v
elif op == 5: # XRA
buf = acm ^ v
elif op == 6: # ORA
buf = acm | v
elif op == 7: # CMP
buf = acm - v
self.check_parity(buf)
self.check_signed(buf)
self.check_zero(buf)
if op not in (1, 3):
self.check_carry(buf)
if op != 7:
self.register.set(7, buf)
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)
v = self.register.get_word(op2)
self.register.set_word(2, d16(hl + v))
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, d16(v))
else: #DCX
v = self.register.get_word(op2) - 1
self.register.set_word(op2, d16(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, d8(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, d8(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 == 0x76: # HLT
self.running = False
else: # MOV
self.register.set(op2, self.register.get(op3))
elif op1 == 2: # ADD/ ADC / SUB / SBB / ANA / XRA / ORA / CMP
self.calc(op2, self.register.get(op3))
elif op1 == 3:
if op3 == 0: # RNZ RNC RPO RP RZ RC RPE RM
if self.check_flg(op2):
addr = self.pop()
self.register.set_word(5, addr)
elif op3 == 1: #POP RET PCHL SPHL
b1 = op2 & 1
b2 = op2 >> 1
if b1 == 0: # POP
v = self.pop()
self.register.set_word(b2, v)
else: # RET PCHL SPHL
if b2 == 2: # PCHL
hl = self.register.get_word(2)
self.register.set_word(5, hl)
elif b2 == 3: # SPHL
hl = self.register.get_word(2)
self.register.set_word(4, hl)
else: # RET
addr = self.pop()
self.register.set_word(5, addr)
elif op3 == 2: #JNZ JNC JPO JP JZ JC JPE JM
addr = self.fetch_word()
if self.check_flg(op2):
self.register.set_word(5, addr)
elif op3 == 3: #JMP OUT XTHL DI JMP IN XCHG EI
if op2 in (0, 1): # JMP
addr = self.fetch_word()
self.register.set_word(5, addr)
elif op2 == 2: #OUT
v = self.fetch()
self.hardware.out_(self.register.get(7), v)
elif op2 == 3: #IN
v = self.fetch()
self.register.set(7, self.hardware.in_(v))
elif op2 == 4: # XTHL
v = self.pop()
self.push(self.register.get_word(2))
self.register.set_word(2, v)
elif op2 == 5: # XCHG
v = self.register.get_word(1)
self.register.set_word(1, self.register.get_word(2))
self.register.set_word(2, v)
pass
elif op2 == 6: # DI
pass
elif op2 == 7: # EI
pass
elif op3 == 4: #CNZ CNC CPO CP CZ CC CPE CM
addr = self.fetch_word()
if self.check_flg(op2):
pc = self.register.get_word(5)
self.push(pc)
self.register.set_word(5, addr)
elif op3 == 5: #PUSH CALL
b1 = op2 & 1
b2 = op2 >> 1
if b1 == 0: # PUSH
v = self.register.get_word(b2)
self.push(v)
else: # CALL
addr = self.fetch_word()
pc = self.register.get_word(5)
self.push(pc)
self.register.set_word(5, addr)
elif op3 == 6: # ADI SUI ANI ORI ACI SBI XRI CPI
self.calc(op2, self.fetch())
elif op3 == 7: # RST
vec = op2 << 3
self.register.set_word(5, addr)
self.register.update()
time.sleep(0.001)
#self.register.monitor()
class Hardware:
def __init__(self):
pass
def in_(self, n):
return 0xff
def out_(self, a, n):
if n == 5:
print(chr(a), end="")
if __name__ == "__main__":
memory = Memory()
register = Register(memory)
hardware = Hardware()
cpu = CPU(memory=memory, register=register, hardware=hardware)
"""
ld hl, msg
loop: ld a,(hl)
out(5), a
inc hl
or a
jp nz, loop
halt
msg: db "Hello, world!", 0
"""
pg = (
0x21, 0x0c, 0x00, 0x7e, 0xd3, 0x05, 0x23, 0xb7,
0xc2, 0x03, 0x00, 0x76, 0x48, 0x65, 0x6c, 0x6c,
0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0x21, 0x0d, 0x0a, 0x00,
)
for i, p in enumerate(pg):
memory.set(i, p)
cpu.mainloop()
# memory monitor
#for i in range(0x10):
# print(format(memory.get(i + 0xfff0), "02X"), end=" ")
#print()
問題としてa16という関数がメモリ用に作ってあるのに16bitレジスタの補正にも利用されていた。
MEMORY_SIZEが0xffffではない場合に計算がずれてしまう。
計算用にはd16という関数を作った。
それに従ってa8はd8に名称を変更。
pushで16bit演算の補正を行っていなかったためアドレスがずれてしまう問題があった。pushしてpopを実行すると値が正しく取れなかった。
まだ問題があるかもしれないけれどもテストしきれていない。
Hello, world!を表示するコードについては動いた。