Execute Computer/Zero: Difference between revisions
Content added Content deleted
m (add second version) |
(Add Python) |
||
Line 643: | Line 643: | ||
0-1: 255 |
0-1: 255 |
||
1+255: 0 |
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> |
</pre> |
||