Execute Computer/Zero: Difference between revisions

Add Python
m (add second version)
(Add Python)
Line 643:
0-1: 255
1+255: 0
</pre>
 
=={{header|Python}}==
<lang python>"""Computer/zero Assembly emulator. Requires Python >= 3.7"""
from typing import Dict
from typing import Iterable
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Tuple
from typing import Union
 
 
NOP = 0b000
LDA = 0b001
STA = 0b010
ADD = 0b011
SUB = 0b100
BRZ = 0b101
JMP = 0b110
STP = 0b111
 
OPCODES = {
"NOP": NOP,
"LDA": LDA,
"STA": STA,
"ADD": ADD,
"SUB": SUB,
"BRZ": BRZ,
"JMP": JMP,
"STP": STP,
}
 
 
class Instruction(NamedTuple):
opcode: Optional[str]
argument: Union[str, int, None]
 
 
Bytecode = Iterable[int]
Instructions = List[Instruction]
Labels = Dict[str, int]
 
 
def parse(assembly: str) -> Tuple[Instructions, Labels]:
instructions: Instructions = []
labels: Dict[str, int] = {}
linenum: int = 0
lines = [line.strip() for line in assembly.split("\n")]
 
for line in lines:
# Discard comments
if ";" in line:
line = line.split(";", 1)[0]
 
# Parse label
if ":" in line:
label, line = line.split(":", 1)
labels[label] = linenum
 
# Parse opcode and argument
instruction = line.strip().split()
if len(instruction) == 1:
if instruction[0] in OPCODES:
# Opcode without argument
opcode: Optional[str] = instruction[0]
argument: Union[str, int, None] = None
elif instruction[0].isnumeric():
# Data, without opcode
opcode = None
argument = int(instruction[0])
else:
# Unexpected
raise Exception(f"unknown instruction '{line}', {linenum}")
elif len(instruction) == 2:
# Opcode and argument
opcode, argument = instruction
 
if opcode not in OPCODES:
raise Exception(f"unknown instruction '{line}', {linenum}")
 
if argument.isnumeric():
argument = int(argument)
else:
# Argument is a label
argument = argument
elif not instruction:
# blank line
opcode = "NOP"
argument = None
else:
raise Exception(f"unknown instruction '{line}', {linenum}")
 
instructions.append(Instruction(opcode, argument))
linenum += 1
 
return instructions, labels
 
 
def compile(instructions: Instructions, labels: Labels) -> Bytecode:
for instruction in instructions:
if isinstance(instruction.argument, str):
argument = labels[instruction.argument]
elif isinstance(instruction.argument, int):
argument = instruction.argument
else:
argument = 0
 
if instruction.opcode:
yield OPCODES[instruction.opcode] << 5 | argument
else:
yield argument
 
 
def run(bytecode: bytes) -> int:
accumulator = 0
program_counter = 0
memory = list(bytecode)[:32] + [0 for _ in range(32 - len(bytecode))]
 
while program_counter < 32:
operation = memory[program_counter] >> 5
argument = memory[program_counter] & 0b11111
program_counter += 1
 
if operation == NOP:
continue
elif operation == LDA:
accumulator = memory[argument]
elif operation == STA:
memory[argument] = accumulator
elif operation == ADD:
accumulator = (accumulator + memory[argument]) % 256
elif operation == SUB:
accumulator = (accumulator - memory[argument]) % 256
elif operation == BRZ:
if accumulator == 0:
program_counter = argument
elif operation == JMP:
program_counter = argument
elif operation == STP:
break
else:
raise Exception(":( " + str(operation))
 
return accumulator
 
 
SAMPLES = [
"""\
LDA x
ADD y ; accumulator = x + y
STP
x: 2
y: 2
""",
"""\
loop: LDA prodt
ADD x
STA prodt
LDA y
SUB one
STA y
BRZ done
JMP loop
done: LDA prodt ; to display it
STP
x: 8
y: 7
prodt: 0
one: 1
""",
"""\
loop: LDA n
STA temp
ADD m
STA n
LDA temp
STA m
LDA count
SUB one
BRZ done
STA count
JMP loop
done: LDA n ; to display it
STP
m: 1
n: 1
temp: 0
count: 8 ; valid range: 1-11
one: 1
""",
"""\
start: LDA load
ADD car ; head of list
STA ldcar
ADD one
STA ldcdr ; next CONS cell
ldcar: NOP
STA value
ldcdr: NOP
BRZ done ; 0 stands for NIL
STA car
JMP start
done: LDA value ; CAR of last CONS
STP
load: LDA 0
value: 0
car: 28
one: 1
; order of CONS cells
; in memory
; does not matter
6
0 ; 0 stands for NIL
2 ; (CADR ls)
26 ; (CDDR ls) -- etc.
5
20
3
30
1 ; value of (CAR ls)
22 ; points to (CDR ls)
4
24
""",
"""\
p: 0 ; NOP in first round
c: 0
start: STP ; wait for p's move
pmove: NOP
LDA pmove
SUB cmove
BRZ same
LDA pmove
STA cmove ; tit for tat
BRZ cdeft
LDA c ; p defected, c did not
ADD three
STA c
JMP start
cdeft: LDA p
ADD three
STA p
JMP start
same: LDA pmove
STA cmove ; tit for tat
LDA p
ADD one
ADD pmove
STA p
LDA c
ADD one
ADD pmove
STA c
JMP start
cmove: 0 ; co-operate initially
one: 1
three: 3
""",
"""\
LDA 3
SUB 4
STP 0
0
255
""",
"""\
LDA 3
SUB 4
STP 0
0
1
""",
"""\
LDA 3
ADD 4
STP 0
1
255
""",
]
 
 
def main() -> None:
for sample in SAMPLES:
instructions, labels = parse(sample.strip())
bytecode = bytes(compile(instructions, labels))
# print(bytecode)
print(run(bytecode))
 
 
if __name__ == "__main__":
main()
</lang>
 
{{out}}
<pre>
4
56
55
6
0
1
255
0
</pre>
 
140

edits