# --------------------------------------------------------- # 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 # --------------------------------------------------------- from dataclasses import dataclass, field import sys @dataclass class CPU: pc: int = 0 sp: int = 255 regs: list = field(default_factory=list) # 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: cycles_added = 0 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) # ----------------- 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 if 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() # --- 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 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 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] 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 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 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}" print(f"[OUT] R{r} = {c.regs[r]}") # --- 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 else: # Si DB est apres RET on n atteind pas ce bloc instr = f"DB 0x{b:02X}" size = 0 # DB n'est pas une INS # 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 return 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) ram = self.dump_ram() return { "pc": pc_before, "instr": instr, "cycles_added": cycles_added, "regs": c.regs.copy(), "lt": c.lt, "eq": c.eq, "sp": c.sp, "ram": ram } # ----------------- boucle principale ----------------- def run(self, max_steps: int = 100000): steps = 0 while self.cpu.running and steps < max_steps: result = self.step() yield result steps += 1 def dump_ram(self): print("\n========= RAM DUMP ========") for addr in range(0, 256, 8): chunk = self.ram[addr:addr+8] hex_values = " ".join(f"{b:02X}" for b in chunk) print(f"{addr:02X}: {hex_values}") print("===========================\n") # --------------------------------------------------------- # 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() sim = Simulator(program) sim.run() else: print("Needs *.bin as parameter")