Execute Computer/Zero is an implementation of Computer/zero Assembly.
Other implementations of Computer/zero Assembly.
Task

Create a Computer/zero Assembly emulator. You may consider this webpage as a reference implementation. Output the results of the sample programs "2+2" and "7*8" found there.

   The virtual machine "bytecode" needs to be able to modify itself (or at least act as though it can) while the virtual machine is running, to be consistent with the reference implementation.
   For output, it is sufficient to have the implementation of the STP opcode return the accumulator to your actual language, and then use your standard printing routines to output it.
Bonus Points
 Run all 5 sample programs at the aforementioned website and output their results.


"""Computer/zero Assembly emulator. Requires Python >= 3.7"""
 
import re
 
from typing import Dict
from typing import Iterable
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Tuple
 
 
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,
}
 
RE_INSTRUCTION = re.compile(
    r"\s*"
    r"(?:(?P<label>\w+):)?"
    r"\s*"
    rf"(?P<opcode>{'|'.join(OPCODES)})?"
    r"\s*"
    r"(?P<argument>\w+)?"
    r"\s*"
    r"(?:;(?P<comment>[\w\s]+))?"
)
 
 
class AssemblySyntaxError(Exception):
    pass
 
 
class Instruction(NamedTuple):
    label: Optional[str]
    opcode: Optional[str]
    argument: Optional[str]
    comment: Optional[str]
 
 
def parse(assembly: str) -> Tuple[List[Instruction], Dict[str, int]]:
    instructions: List[Instruction] = []
    labels: Dict[str, int] = {}
    linenum: int = 0
 
    for line in assembly.split("\n"):
        match = RE_INSTRUCTION.match(line)
 
        if not match:
            raise AssemblySyntaxError(f"{line}: {linenum}")
 
        instructions.append(Instruction(**match.groupdict()))
        label = match.group(1)
        if label:
            labels[label] = linenum
 
        linenum += 1
 
    return instructions, labels
 
 
def compile(instructions: List[Instruction], labels: Dict[str, int]) -> Iterable[int]:
    for instruction in instructions:
        if instruction.argument is None:
            argument = 0
        elif instruction.argument.isnumeric():
            argument = int(instruction.argument)
        else:
            argument = labels[instruction.argument]
 
        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(f"error: {operation} {argument}")
 
    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)
        bytecode = bytes(compile(instructions, labels))
        result = run(bytecode)
        print(result)
 
 
if __name__ == "__main__":
    main()
 