# MINI COMPUTER EMULATOR
# Command-line virtual computer with an interactive assembly editor.
MEMORY_SIZE = 256
REGISTERS = ("A", "B", "C", "D")
MAX_STEPS = 10000
HELP = """
INSTRUCTIONS
SET A, 10 Set register A to 10
MOV A, B Copy register B into A
ADD A, B Add B to A
SUB A, B Subtract B from A
INC A Increment A
DEC A Decrement A
LOAD A, 10 Read memory address 10 into A
STORE A, 10 Write A into memory address 10
JMP LOOP Jump to a label
JNZ A, LOOP Jump if A is not zero
JZ LOOP Jump if the zero flag is set
PRINT A Print register A
HALT Stop the program
Use labels like LOOP:
Use ; for comments.
"""
class Assembler:
ARITY = {
"SET": 2, "MOV": 2, "ADD": 2, "SUB": 2,
"INC": 1, "DEC": 1, "LOAD": 2, "STORE": 2,
"JMP": 1, "JNZ": 2, "JZ": 1, "PRINT": 1,
"HALT": 0,
}
def assemble(self, source):
lines = []
labels = {}
for line_no, raw in enumerate(source.splitlines(), 1):
line = raw.split(";", 1)[0].strip()
if not line:
continue
if ":" in line:
label, line = line.split(":", 1)
label = label.strip().upper()
if label in labels:
raise ValueError(
f"Line {line_no}: duplicate label {label}"
)
labels[label] = len(lines)
line = line.strip()
if not line:
continue
parts = line.split(None, 1)
op = parts[0].upper()
args = (
[x.strip().upper() for x in parts[1].split(",")]
if len(parts) > 1 else []
)
lines.append((op, args, line_no))
if len(lines) > MEMORY_SIZE:
raise ValueError("Maximum program size is 256 instructions.")
program = []
for address, (op, args, line_no) in enumerate(lines):
if op not in self.ARITY:
raise ValueError(f"Line {line_no}: unknown instruction {op}")
if len(args) != self.ARITY[op]:
raise ValueError(f"Line {line_no}: incorrect operand count")
args = [labels.get(arg, arg) for arg in args]
reg_positions = {
"SET": (0,), "MOV": (0, 1), "ADD": (0, 1),
"SUB": (0, 1), "INC": (0,), "DEC": (0,),
"LOAD": (0,), "STORE": (0,), "JNZ": (0,),
"PRINT": (0,),
}.get(op, ())
for pos in reg_positions:
if args[pos] not in REGISTERS:
raise ValueError(
f"Line {line_no}: invalid register {args[pos]}"
)
numeric_positions = {
"SET": (1,), "LOAD": (1,), "STORE": (1,),
"JMP": (0,), "JNZ": (1,), "JZ": (0,),
}.get(op, ())
for pos in numeric_positions:
try:
value = int(args[pos], 0)
except ValueError:
raise ValueError(
f"Line {line_no}: invalid number or label {args[pos]}"
)
if op == "SET" and not 0 <= value <= 255:
raise ValueError("SET values must be 0-255.")
if op in ("LOAD", "STORE") and not 0 <= value < MEMORY_SIZE:
raise ValueError("Memory addresses must be 0-255.")
if op in ("JMP", "JNZ", "JZ") and not (
0 <= value < len(lines)
):
raise ValueError("Invalid jump destination.")
program.append((op, args, line_no))
return program
class Computer:
def __init__(self):
self.reset()
def reset(self):
self.memory = [0] * MEMORY_SIZE
self.registers = {r: 0 for r in REGISTERS}
self.pc = 0
self.zero = False
self.halted = False
self.steps = 0
self.output = []
def execute(self, op, args):
r = self.registers
def setr(name, value):
r[name] = value & 255
self.zero = r[name] == 0
def address(text):
value = int(text)
if not 0 <= value < MEMORY_SIZE:
raise ValueError("Memory address must be 0-255.")
return value
if op == "SET":
setr(args[0], int(args[1]))
elif op == "MOV":
setr(args[0], r[args[1]])
elif op == "ADD":
setr(args[0], r[args[0]] + r[args[1]])
elif op == "SUB":
setr(args[0], r[args[0]] - r[args[1]])
elif op == "INC":
setr(args[0], r[args[0]] + 1)
elif op == "DEC":
setr(args[0], r[args[0]] - 1)
elif op == "LOAD":
setr(args[0], self.memory[address(args[1])])
elif op == "STORE":
self.memory[address(args[1])] = r[args[0]]
elif op == "JMP":
self.pc = int(args[0])
elif op == "JNZ":
if r[args[0]] != 0:
self.pc = int(args[1])
elif op == "JZ":
if self.zero:
self.pc = int(args[0])
elif op == "PRINT":
message = str(r[args[0]])
self.output.append(message)
print("OUTPUT:", message)
elif op == "HALT":
self.halted = True
def run(self, program, step_mode=False):
self.reset()
while not self.halted:
if not 0 <= self.pc < len(program):
print("Program ended.")
break
if self.steps >= MAX_STEPS:
print("Execution limit reached.")
break
address = self.pc
op, args, line_no = program[address]
self.pc += 1
self.steps += 1
print(f"\n[{self.steps:03}] PC={address:03} {op} {args}")
self.execute(op, args)
print("REGISTERS:", self.registers)
if step_mode and not self.halted:
input("Press Enter for the next instruction...")
print("\n--- FINAL STATE ---")
print("Registers:", self.registers)
print("Program counter:", self.pc)
print("Zero flag:", self.zero)
print("Memory [0:16]:", self.memory[:16])
print("Instructions executed:", self.steps)
def interactive_editor():
print("\n=== MINI COMPUTER EMULATOR ===")
print("Enter assembly code. Type END on a line to finish.")
print("Type HELP to see instructions, or EXIT to quit.\n")
while True:
command = input("computer> ").strip().upper()
if command == "EXIT":
return
if command == "HELP":
print(HELP)
continue
if command != "EDIT":
print("Commands: EDIT, HELP, EXIT")
continue
print("\n--- ASSEMBLY EDITOR ---")
print("Enter your program one line at a time.")
print("Type END on its own line when finished.\n")
lines = []
while True:
line = input(f"{len(lines) + 1:03}> ")
if line.strip().upper() == "END":
break
lines.append(line)
source = "\n".join(lines)
try:
program = Assembler().assemble(source)
except ValueError as error:
print("ASSEMBLY ERROR:", error)
continue
print(f"\nAssembled {len(program)} instructions.")
print("1 = Run")
print("2 = Step through instructions")
print("3 = Edit again")
choice = input("Choose: ").strip()
if choice in ("1", "2"):
computer = Computer()
computer.run(program, step_mode=(choice == "2"))
elif choice == "3":
continue
else:
print("Returning to the command prompt.")
if __name__ == "__main__":
interactive_editor()3 views