Browse Source

Break up instruction tests and add more

master
Forest Belton 2 years ago
parent
commit
0c3f9241ef
7 changed files with 485 additions and 19 deletions
  1. +11
    -3
      gbso/cpu.py
  2. +113
    -3
      gbso/insn.py
  3. +0
    -0
      tests/insn/__init__.py
  4. +12
    -0
      tests/insn/helpers.py
  5. +288
    -0
      tests/insn/test_alu8.py
  6. +59
    -0
      tests/insn/test_loads16.py
  7. +2
    -13
      tests/insn/test_loads8.py

+ 11
- 3
gbso/cpu.py View File

@ -8,6 +8,7 @@ class CPU:
carry: int
cycles: int
reg8: Dict[R8, int]
sp: int
memory: bytearray
def __init__(self) -> None:
@ -20,12 +21,19 @@ class CPU:
return self.reg8[r]
def get_reg16(self, rr: R16) -> int:
if rr == R16.SP:
return self.sp
return (self.reg8[R16_HI[rr]] << 8) | self.reg8[R16_LO[rr]]
def set_reg8(self, r: R8, n: int) -> None:
self.reg8[r] = n & 0xFF
def set_reg16(self, rr: R16, nn: int) -> None:
if rr == R16.SP:
self.sp = nn & 0xFFFF
return
self.reg8[R16_HI[rr]] = (nn >> 8) & 0xFF
self.reg8[R16_LO[rr]] = nn & 0xFF
@ -33,14 +41,14 @@ class CPU:
return self.memory[nn & 0xFFFF]
def get_mem16(self, nn: int) -> int:
return (self.memory[(nn + 1) & 0xFF] << 8) | self.memory[nn & 0xFFFF]
return (self.memory[nn & 0xFFFF] << 8) | self.memory[(nn + 1) & 0xFFFF]
def set_mem8(self, nn: int, n: int) -> None:
self.memory[nn & 0xFFFF] = n & 0xFF
def set_mem16(self, nn: int, nn1: int) -> None:
self.memory[nn & 0xFFFF] = nn1 & 0xFF
self.memory[(nn + 1) & 0xFFFF] = (nn1 >> 8) & 0xFF
self.memory[nn & 0xFFFF] = (nn1 >> 8) & 0xFF
self.memory[(nn + 1) & 0xFFFF] = nn1 & 0xFF
def deref_hl(self) -> int:
return self.get_mem8(self.get_reg16(R16.HL))

+ 113
- 3
gbso/insn.py View File

@ -240,6 +240,7 @@ class LD_RR_NN(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(self.rr, self.nn)
cpu.cycles += 12
def pretty(self) -> str:
return f"LD {self.rr.value}, {hex(self.nn)}"
@ -251,6 +252,7 @@ class LD_NN_SP(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_mem16(self.nn, cpu.get_reg16(R16.SP))
cpu.cycles += 20
def pretty(self) -> str:
return f"LD ({hex(self.nn)}), SP"
@ -260,19 +262,23 @@ class LD_NN_SP(Insn):
class LD_SP_HL(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(R16.SP, cpu.get_reg16(R16.HL))
cpu.cycles += 8
def pretty(self) -> str:
return "LD SP, HL"
# NOTE: Normally we would support PUSH AF, but then non-carry flags could impact
# the program (by popping or inspecting where F was stored)
@dataclass
class PUSH_RR(Insn):
rr: R16
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP)
cpu.set_reg16(R16.SP, sp - 2)
cpu.set_mem16(sp - 2, cpu.get_reg16(self.rr))
sp = cpu.get_reg16(R16.SP) - 2
cpu.set_reg16(R16.SP, sp)
cpu.set_mem16(sp, cpu.get_reg16(self.rr))
cpu.cycles += 16
def pretty(self) -> str:
return f"PUSH {self.rr.value}"
@ -286,6 +292,7 @@ class POP_RR(Insn):
sp = cpu.get_reg16(R16.SP)
cpu.set_reg16(self.rr, cpu.get_mem16(sp))
cpu.set_reg16(R16.SP, sp + 2)
cpu.cycles += 12
def pretty(self) -> str:
return f"POP {self.rr.value}"
@ -299,6 +306,7 @@ class ADD_A_R(Insn):
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r)
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 4
def pretty(self) -> str:
return f"ADD A, {self.r.value}"
@ -312,6 +320,7 @@ class ADD_A_N(Insn):
a = cpu.get_reg8(R8.A) + self.n
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"ADD A, {hex(self.n)}"
@ -323,6 +332,7 @@ class ADD_A_HL(Insn):
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"ADD A, (HL)"
@ -336,6 +346,7 @@ class ADC_A_R(Insn):
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r) + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 4
def pretty(self) -> str:
return f"ADC A, {self.r.value}"
@ -349,6 +360,7 @@ class ADC_A_N(Insn):
a = cpu.get_reg8(R8.A) + self.n + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"ADC A, {hex(self.n)}"
@ -360,6 +372,7 @@ class ADC_A_HL(Insn):
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL)) + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"ADC A, (HL)"
@ -373,6 +386,7 @@ class SUB_A_R(Insn):
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r)
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 4
def pretty(self) -> str:
return f"SUB A, {self.r.value}"
@ -386,6 +400,7 @@ class SUB_A_N(Insn):
a = cpu.get_reg8(R8.A) - self.n
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"SUB A, {hex(self.n)}"
@ -397,6 +412,7 @@ class SUB_A_HL(Insn):
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return "SUB A, (HL)"
@ -410,6 +426,7 @@ class SBC_A_R(Insn):
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r) - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 4
def pretty(self) -> str:
return f"SBC A, {self.r.value}"
@ -423,6 +440,7 @@ class SBC_A_N(Insn):
a = cpu.get_reg8(R8.A) - self.n - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return f"SBC A, {hex(self.n)}"
@ -434,6 +452,7 @@ class SBC_A_HL(Insn):
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL)) - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
cpu.cycles += 8
def pretty(self) -> str:
return "SBC A, (HL)"
@ -447,6 +466,7 @@ class AND_A_R(Insn):
a = cpu.get_reg8(R8.A) & cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 4
def pretty(self) -> str:
return f"AND A, {self.r.value}"
@ -460,6 +480,7 @@ class AND_A_N(Insn):
a = cpu.get_reg8(R8.A) & self.n
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return f"AND A, {hex(self.n)}"
@ -471,17 +492,99 @@ class AND_A_HL(Insn):
a = cpu.get_reg8(R8.A) & cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return "AND A, (HL)"
@dataclass
class XOR_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 4
def pretty(self) -> str:
return f"XOR A, {self.r.value}"
@dataclass
class XOR_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ self.n
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return f"XOR A, {hex(self.n)}"
@dataclass
class XOR_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return "XOR A, (HL)"
@dataclass
class OR_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 4
def pretty(self) -> str:
return f"OR A, {self.r.value}"
@dataclass
class OR_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | self.n
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return f"OR A, {hex(self.n)}"
@dataclass
class OR_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.carry = 0
cpu.cycles += 8
def pretty(self) -> str:
return "OR A, (HL)"
@dataclass
class CP_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
cpu.carry = 1 if cpu.get_reg8(R8.A) < cpu.get_reg8(self.r) else 0
cpu.cycles += 4
def pretty(self) -> str:
return f"CP A, {self.r.value}"
@ -493,6 +596,7 @@ class CP_A_N(Insn):
def exec(self, cpu: CPU) -> None:
cpu.carry = 1 if cpu.get_reg8(R8.A) < self.n else 0
cpu.cycles += 8
def pretty(self) -> str:
return f"CP A, {hex(self.n)}"
@ -502,6 +606,7 @@ class CP_A_N(Insn):
class CP_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
cpu.carry = 1 if cpu.get_reg8(R8.A) < cpu.get_mem8(cpu.get_reg16(R16.HL)) else 0
cpu.cycles += 8
def pretty(self) -> str:
return "CP A, (HL)"
@ -513,6 +618,7 @@ class INC_R(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) + 1)
cpu.cycles += 4
def pretty(self) -> str:
return f"INC {self.r.value}"
@ -525,6 +631,7 @@ class INC_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) + 1)
cpu.cycles += 12
def pretty(self) -> str:
return "INC (HL)"
@ -536,6 +643,7 @@ class DEC_R(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) - 1)
cpu.cycles += 4
def pretty(self) -> str:
return f"DEC {self.r.value}"
@ -548,6 +656,7 @@ class DEC_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) - 1)
cpu.cycles += 12
def pretty(self) -> str:
return "DEC (HL)"
@ -557,6 +666,7 @@ class DEC_HL(Insn):
class CPL(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_reg8(R8.A) ^ 0xFF)
cpu.cycles += 4
def pretty(self) -> str:
return "CPL"

+ 0
- 0
tests/insn/__init__.py View File


+ 12
- 0
tests/insn/helpers.py View File

@ -0,0 +1,12 @@
from gbso.insn import *
from gbso.regs import *
def n8():
for i in range(0xFF + 1):
yield i
def n16():
for i in range(0xFFFF + 1):
yield i

+ 288
- 0
tests/insn/test_alu8.py View File

@ -0,0 +1,288 @@
import pytest
from tests.insn.helpers import *
@pytest.mark.parametrize("r", set(R8) - {R8.A})
def test_add_a_r(r):
cpu = CPU()
cpu.set_reg8(r, 0x7F)
ADD_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 4
cpu.set_reg8(r, 0x81)
ADD_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x00
assert cpu.carry == 1
assert cpu.cycles == 8
@pytest.mark.parametrize("n", n8())
def test_add_a_n(n):
cpu = CPU()
cpu.set_reg8(R8.A, 0x7F)
ADD_A_N(n).exec(cpu)
assert cpu.get_reg8(R8.A) == (0x7F + n) & 0xFF
assert cpu.carry == (1 if n >= 0x81 else 0)
assert cpu.cycles == 8
def test_add_a_hl():
cpu = CPU()
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, 0x7F)
ADD_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 8
cpu.set_mem8(0x1234, 0x81)
ADD_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0
assert cpu.carry == 1
assert cpu.cycles == 16
# TODO: ADC_A_R, ADC_A_N, ADC_A_HL, SUB_A_R, SUB_A_N, SUB_A_HL, SBC_A_R,
# SBC_A_N, SBC_A_HL
@pytest.mark.parametrize("r", set(R8) - {R8.A})
def test_and_a_r(r):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg8(r, 0x7F)
AND_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12
assert cpu.carry == 0
assert cpu.cycles == 4
@pytest.mark.parametrize("n", n8())
def test_and_a_n(n):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
AND_A_N(n).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12 & n
assert cpu.carry == 0
assert cpu.cycles == 8
def test_and_a_hl():
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, 0x7F)
AND_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12
assert cpu.carry == 0
assert cpu.cycles == 8
@pytest.mark.parametrize("r", set(R8) - {R8.A})
def test_xor_a_r(r):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg8(r, 0x7F)
XOR_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x6D
assert cpu.carry == 0
assert cpu.cycles == 4
@pytest.mark.parametrize("n", n8())
def test_xor_a_n(n):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
XOR_A_N(n).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12 ^ n
assert cpu.carry == 0
assert cpu.cycles == 8
def test_xor_a_hl():
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, 0x7F)
XOR_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x6D
assert cpu.carry == 0
assert cpu.cycles == 8
@pytest.mark.parametrize("r", set(R8) - {R8.A})
def test_or_a_r(r):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg8(r, 0x7F)
OR_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 4
@pytest.mark.parametrize("n", n8())
def test_or_a_n(n):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
OR_A_N(n).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12 | n
assert cpu.carry == 0
assert cpu.cycles == 8
def test_or_a_hl():
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, 0x7F)
OR_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 8
@pytest.mark.parametrize("r", set(R8) - {R8.A})
def test_cp_a_r(r):
cpu = CPU()
cpu.set_reg8(R8.A, 0x7F)
cpu.set_reg8(r, 0x12)
CP_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 4
cpu.set_reg8(r, 0x80)
CP_A_R(r).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 1
assert cpu.cycles == 8
@pytest.mark.parametrize("n", n8())
def test_cp_a_n(n):
cpu = CPU()
cpu.set_reg8(R8.A, 0x12)
CP_A_N(n).exec(cpu)
assert cpu.get_reg8(R8.A) == 0x12
assert cpu.carry == (1 if 0x12 < n else 0)
assert cpu.cycles == 8
def test_cp_a_hl():
cpu = CPU()
cpu.set_reg8(R8.A, 0x7F)
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, 0x12)
CP_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 0
assert cpu.cycles == 8
cpu.set_mem8(0x1234, 0x80)
CP_A_HL().exec(cpu)
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.carry == 1
assert cpu.cycles == 16
@pytest.mark.parametrize("r,n", [(r, n) for r in R8 for n in n8()])
def test_inc_r(r, n):
cpu = CPU()
cpu.set_reg8(r, n)
INC_R(r).exec(cpu)
assert cpu.get_reg8(r) == (n + 1) & 0xFF
assert cpu.cycles == 4
@pytest.mark.parametrize("n", n8())
def test_inc_hl(n):
cpu = CPU()
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, n)
INC_HL(n).exec(cpu)
assert cpu.deref_hl() == (n + 1) & 0xFF
assert cpu.cycles == 12
@pytest.mark.parametrize("r,n", [(r, n) for r in R8 for n in n8()])
def test_dec_r(r, n):
cpu = CPU()
cpu.set_reg8(r, n)
DEC_R(r).exec(cpu)
assert cpu.get_reg8(r) == (n - 1) & 0xFF
assert cpu.cycles == 4
@pytest.mark.parametrize("n", n8())
def test_dec_hl(n):
cpu = CPU()
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, n)
DEC_HL(n).exec(cpu)
assert cpu.deref_hl() == (n - 1) & 0xFF
assert cpu.cycles == 12
# TODO: Test DAA after implementing it
@pytest.mark.parametrize("n", n8())
def test_cpl(n):
cpu = CPU()
cpu.set_reg8(R8.A, n)
CPL().exec(cpu)
assert cpu.get_reg8(R8.A) == n ^ 0xFF
assert cpu.cycles == 4

+ 59
- 0
tests/insn/test_loads16.py View File

@ -0,0 +1,59 @@
import pytest
from tests.insn.helpers import *
@pytest.mark.parametrize("rr", set(R16) - {R16.AF})
def test_ld_rr_nn(rr):
cpu = CPU()
LD_RR_NN(rr, 0x1234).exec(cpu)
assert cpu.get_reg16(rr) == 0x1234
assert cpu.cycles == 12
def test_ld_nn_sp():
cpu = CPU()
cpu.set_reg16(R16.SP, 0xABCD)
LD_NN_SP(0x1234).exec(cpu)
assert cpu.get_mem16(0x1234) == 0xABCD
assert cpu.cycles == 20
def test_ld_sp_hl():
cpu = CPU()
cpu.set_reg16(R16.HL, 0x1234)
LD_SP_HL().exec(cpu)
assert cpu.get_reg16(R16.SP) == 0x1234
assert cpu.cycles == 8
@pytest.mark.parametrize("rr", set(R16) - {R16.AF, R16.SP})
def test_push_rr(rr):
cpu = CPU()
cpu.set_reg16(R16.SP, 0xFFFF)
cpu.set_reg16(rr, 0x1234)
PUSH_RR(rr).exec(cpu)
assert cpu.get_reg16(R16.SP) == 0xFFFD
assert cpu.get_mem16(0xFFFD) == 0x1234
assert cpu.cycles == 16
@pytest.mark.parametrize("rr", set(R16) - {R16.AF, R16.SP})
def test_pop_rr(rr):
cpu = CPU()
cpu.set_reg16(R16.SP, 0xFFFD)
cpu.set_mem16(0xFFFD, 0x1234)
POP_RR(rr).exec(cpu)
assert cpu.get_reg16(R16.SP) == 0xFFFF
assert cpu.get_reg16(rr) == 0x1234
assert cpu.cycles == 12

tests/test_insn.py → tests/insn/test_loads8.py View File

@ -1,17 +1,6 @@
import pytest
from gbso.insn import *
from gbso.regs import *
def n8():
for i in range(0xFF + 1):
yield i
def n16():
for i in range(0xFFFF + 1):
yield i
from tests.insn.helpers import *
@pytest.mark.parametrize("dst,src", [(x, y) for x in R8 for y in R8])
@ -229,4 +218,4 @@ def test_ldd_a_hl():
assert cpu.get_reg16(R16.HL) == 0x1233
assert cpu.get_reg8(R8.A) == 0x7F
assert cpu.cycles == 8
assert cpu.cycles == 8

Loading…
Cancel
Save