24H_du_code_2026/Interpreteur.py
2026-03-22 07:18:37 +01:00

301 lines
9.2 KiB
Python

# ---------------------------------------------------------
# Simulateur
# ---------------------------------------------------------
# - Bus données : 8 bits
# - 4 registres R0..R3 (8 bits)
# - Bus adresse : 8 bits
# - RAM : 256 octets
# - Instructions : 1 ou 2 octets
# - Cycles : 1 octet -> 1, 2 octets -> 2, LDR/STR -> 3
# - PC démarre à 0
# - Pile descendante, SP=255
# ---------------------------------------------------------
import sys, time
import uasyncio as asyncio
class CPU:
pc: int = 0
sp: int = 255
regs: list = [0, 0, 0, 0] # R0..R3
lt: int = 0 # flag LT
eq: int = 0 # flag EQ
cycles: int = 0
running: bool = True
after_ret: bool = False
def __post_init__(self):
if not self.regs:
self.regs = [0, 0, 0, 0]
class Simulator:
def __init__(self, program: bytes):
self.ram = bytearray(256)
for i, b in enumerate(program[:256]):
self.ram[i] = b
self.cpu = CPU()
self.program_size = len(program)
self.motorCallback = None
self.registerCallback = None
# ---- Getter / Setter pour interfacer le robot
def setMotorCallback(self, callback):
self.motorCallback = callback
def setRegisterCallback(self, callback):
self.registerCallback = callback
# ----------------- utilitaires mémoire / pile -----------------
def fetch_byte(self) -> int:
b = self.ram[self.cpu.pc]
self.cpu.pc = (self.cpu.pc + 1) & 0xFF
return b
def push(self, value: int):
if self.cpu.sp < 0:
raise RuntimeError("STACK OVERFLOW")
self.ram[self.cpu.sp] = value & 0xFF
self.cpu.sp -= 1
def pop(self) -> int:
if self.cpu.sp >= 255:
return 0
self.cpu.sp += 1
return self.ram[self.cpu.sp]
# ----------------- exécution d'une instruction -----------------
def step(self):
c = self.cpu
pc_before = c.pc
b = self.fetch_byte()
instr = ""
size = 1 # taille en octets (1 ou 2)
extra_cycles = 0 # pour LDR/STR/TIM
# --- instructions 2 octets à opcode fixe ---
#print(pc_before)
#print(self.program_size)
if c.after_ret:
instr = f"DB 0x{b:02X}"
elif b == 0x00: # CALL _label
addr = self.fetch_byte()
size = 2
instr = f"CALL {addr}"
self.push(c.pc)
c.pc = addr
elif b == 0x40: # JMP _label
addr = self.fetch_byte()
size = 2
instr = f"JMP {addr}"
c.pc = addr
elif b == 0xC0: # JLT _label
addr = self.fetch_byte()
size = 2
instr = f"JLT {addr}"
if c.lt == 1:
c.pc = addr
elif b == 0x20: # JEQ _label
addr = self.fetch_byte()
size = 2
instr = f"JEQ {addr}"
if c.eq == 1:
c.pc = addr
elif b == 0x80: # RET
instr = "RET"
ret = self.pop()
if c.sp >= 255 and ret == 0:
c.after_ret = True
c.running = False
else:
c.pc = ret
# --- PUSH / POP ---
elif (b & 0b11111100) == 0b10100000: # PUSH Rx
r = b & 0b11
instr = f"PUSH R{r}"
self.push(c.regs[r])
elif (b & 0b11111100) == 0b01100000: # POP Rx
r = b & 0b11
instr = f"POP R{r}"
c.regs[r] = self.pop()
if (self.registerCallback != None):
self.registerCallback(r, c.regs[r])
# --- MOV Rx valeur / SUB Rx valeur / CMP Rx valeur ---
elif (b & 0b11111100) == 0b11100000: # MOV Rx valeur
r = b & 0b11
imm = self.fetch_byte()
size = 2
instr = f"MOV R{r}, {imm}"
c.regs[r] = imm
if (self.registerCallback != None):
self.registerCallback(r, c.regs[r])
elif (b & 0b11111100) == 0b00010000: # SUB Rx valeur
r = b & 0b11
imm = self.fetch_byte()
size = 2
instr = f"SUB R{r}, {imm}"
c.regs[r] = (c.regs[r] - imm) & 0xFF
if (self.registerCallback != None):
self.registerCallback(r, c.regs[r])
elif (b & 0b11111100) == 0b10010000: # CMP Rx valeur
r = b & 0b11
imm = self.fetch_byte()
size = 2
instr = f"CMP R{r}, {imm}"
v = c.regs[r]
c.lt = 1 if v < imm else 0
c.eq = 1 if v == imm else 0
# --- MOV / SUB / CMP registre-registre ---
elif (b & 0b11110000) == 0b01010000: # MOV Rx Ry
dst = (b >> 2) & 0b11
src = b & 0b11
instr = f"MOV R{dst}, R{src}"
c.regs[dst] = c.regs[src]
if (self.registerCallback != None):
self.registerCallback(dst, c.regs[dst])
elif (b & 0b11110000) == 0b11010000: # SUB Rx Ry
dst = (b >> 2) & 0b11
src = b & 0b11
instr = f"SUB R{dst}, R{src}"
c.regs[dst] = (c.regs[dst] - c.regs[src]) & 0xFF
if (self.registerCallback != None):
self.registerCallback(dst, c.regs[dst])
elif (b & 0b11110000) == 0b00110000: # CMP Rx Ry
dst = (b >> 2) & 0b11
src = b & 0b11
instr = f"CMP R{dst}, R{src}"
v1 = c.regs[dst]
v2 = c.regs[src]
c.lt = 1 if v1 < v2 else 0
c.eq = 1 if v1 == v2 else 0
# --- LDR / STR (2 octets, 3 cycles) ---
elif (b & 0b11110000) == 0b10110000: # LDR Rx Ry _label
dst = (b >> 2) & 0b11
src = b & 0b11
addr = self.fetch_byte()
size = 2
instr = f"LDR R{dst}, R{src}, {addr}"
eff = (addr + c.regs[src]) & 0xFF
c.regs[dst] = self.ram[eff]
extra_cycles = 1 # 2 octets -> 2 cycles +1 = 3
if (self.registerCallback != None):
self.registerCallback(dst, c.regs[dst])
elif (b & 0b11110000) == 0b01110000: # STR Rx Ry _label
dst = (b >> 2) & 0b11
src = b & 0b11
addr = self.fetch_byte()
size = 2
instr = f"STR R{dst}, R{src}, {addr}"
eff = (addr + c.regs[src]) & 0xFF
self.ram[eff] = c.regs[dst] & 0xFF
extra_cycles = 1
# --- OUT Rx ---
elif (b & 0b11111100) == 0b11110000: # OUT Rx
r = b & 0b11
instr = f"OUT R{r}"
registre = c.regs[r]
print(f"[OUT] R{r} = {registre}")
if (self.motorCallback != None):
motG = (registre >> 4) & 0b1111
motD = (registre) & 0b1111
self.motorCallback(motG, motD)
# --- TIM valeur ---
elif b == 0xF8: # TIM
second = self.fetch_byte()
size = 2
m = (second >> 7) & 0x1
v = second & 0x7F
instr = f"TIM m={m}, v={v}"
mult = 1 if m == 0 else 100
pause_ms = mult * (v + 1)
c.cycles += pause_ms # modélisation de la pause
# print(f"Sleep {pause_ms}ms...")
time.sleep(pause_ms/1000)
# print("BIPBIP")
# if pc_before >= self.program_size:
# if 32 <= b <= 126:
# instr = f"DB 0x{b:02X} ('{chr(b)}')"
# else:
# instr = f"DB 0x{b:02X}"
else:
instr = f"UNKNOWN 0x{b:02X}"
c.running = False
# calcul des cycles
if (b & 0b11110000) in (0xB0, 0x70): # LDR / STR
c.cycles += 3
cycles_added = 3
else:
c.cycles += size
cycles_added = size
self.report(pc_before, instr, cycles_added)
# ----------------- rapport d'exécution -----------------
def report(self, pc_before: int, instr: str, cycles_added: int):
c = self.cpu
regs_str = " ".join(f"R{i}={c.regs[i]:02X}" for i in range(4))
# print(f"PC={pc_before:02X} {instr:20s} +Cycles={cycles_added:3d} Total={c.cycles}")
# print(f" {regs_str} LT={c.lt} EQ={c.eq} SP={c.sp}")
# print("-" * 60)
# ----------------- boucle principale -----------------
def run(self, max_steps: int = 100000):
steps = 0
while self.cpu.running and steps < max_steps:
self.step()
steps += 1
def StartCPU(program, callback, registerCallback):
sim = Simulator(program)
sim.setMotorCallback(callback)
sim.setRegisterCallback(registerCallback)
while sim.cpu.running:
sim.run(max_steps = 1)
#time.sleep(0.1)
import time
# ---------------------------------------------------------
# LECTURE D'UN FICHIER .bin ET LANCEMENT
# ---------------------------------------------------------
if __name__ == "__main__":
# Nom du fichier binaire à exécuter
path =""
args= sys.argv
if (len(args) > 1):
filename = args[1]
print("filename: " + filename)
with open(filename, "rb") as f:
program = f.read()
StartCPU(program, None, None)
else:
print("Needs *.bin as parameter")