Task: Execute Computer_Zero




Go: package main

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

const (
	NOP = iota
	LDA
	STA
	ADD
	SUB
	BRZ
	JMP
	STP
)

var opcodes = map[string]int{
	"NOP": NOP,
	"LDA": LDA,
	"STA": STA,
	"ADD": ADD,
	"SUB": SUB,
	"BRZ": BRZ,
	"JMP": JMP,
	"STP": STP,
}

var reIns = regexp.MustCompile(
	`\s*(?:(\w+):)?\s*` + 
		`(NOP|LDA|STA|ADD|SUB|BRZ|JMP|STP)?\s*` + 
		`(\w+)?\s*` + 
		`(?:;([\w\s]+))?`) 

type ByteCode [32]int

type instruction struct {
	Label  string
	Opcode string
	Arg    string
}

type Program struct {
	Instructions []instruction
	Labels       map[string]int
}

func newInstruction(line string) (*instruction, error) {
	match := reIns.FindStringSubmatch(line)
	if match == nil {
		return nil, fmt.Errorf("syntax error: '%s'", line)
	}
	return &instruction{Label: match[1], Opcode: match[2], Arg: match[3]}, nil
}

func Parse(asm io.Reader) (*Program, error) {
	var p Program
	p.Labels = make(map[string]int, 32)
	scanner := bufio.NewScanner(asm)
	lineNumber := 0

	for scanner.Scan() {
		if instruction, err := newInstruction(scanner.Text()); err != nil {
			return &p, err
		} else {
			if instruction.Label != "" {
				p.Labels[instruction.Label] = lineNumber
			}
			p.Instructions = append(p.Instructions, *instruction)
			lineNumber++
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return &p, nil
}

func (p *Program) Compile() (ByteCode, error) {
	var bytecode ByteCode
	var arg int
	for i, ins := range p.Instructions {
		if ins.Arg == "" {
			arg = 0
		} else if addr, err := strconv.Atoi(ins.Arg); err == nil {
			arg = addr
		} else if addr, ok := p.Labels[ins.Arg]; ok {
			arg = addr
		} else {
			return bytecode, fmt.Errorf("unknown label %v", ins.Arg)
		}

		if opcode, ok := opcodes[ins.Opcode]; ok {
			bytecode[i] = opcode<<5 | arg
		} else {
			bytecode[i] = arg
		}
	}
	return bytecode, nil
}

func floorMod(a, b int) int {
	return ((a % b) + b) % b
}

func Run(bytecode ByteCode) (int, error) {
	acc := 0
	pc := 0
	mem := bytecode

	var op int
	var arg int

loop:
	for pc < 32 {
		op = mem[pc] >> 5
		arg = mem[pc] & 31
		pc++

		switch op {
		case NOP:
			continue
		case LDA:
			acc = mem[arg]
		case STA:
			mem[arg] = acc
		case ADD:
			acc = floorMod(acc+mem[arg], 256)
		case SUB:
			acc = floorMod(acc-mem[arg], 256)
		case BRZ:
			if acc == 0 {
				pc = arg
			}
		case JMP:
			pc = arg
		case STP:
			break loop
		default:
			return acc, fmt.Errorf("runtime error: %v %v", op, arg)
		}
	}

	return acc, nil
}

func Execute(asm string) (int, error) {
	program, err := Parse(strings.NewReader(asm))
	if err != nil {
		return 0, fmt.Errorf("assembly error: %v", err)
	}

	bytecode, err := program.Compile()
	if err != nil {
		return 0, fmt.Errorf("compilation error: %v", err)
	}

	result, err := Run(bytecode)
	if err != nil {
		return 0, err
	}

	return result, nil
}

func main() {
	examples := []string{
		`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`,
		`LDA  3
SUB  4
STP  0
	 0
	 255`,
		`LDA  3
SUB  4
STP  0
		0
		1`,
		`LDA  3
ADD  4
STP  0
		1
		255`,
	}

	for _, asm := range examples {
		if result, err := Execute(asm); err == nil {
			fmt.Println(result)
		} else {
			fmt.Println(err)
		}
	}
}