Execute Computer/Zero: Difference between revisions
Content added Content deleted
m (RPL: optimized version) |
(Add Go) |
||
Line 557: | Line 557: | ||
0 1 : NOP 0 -> 0 2 |
0 1 : NOP 0 -> 0 2 |
||
0 2 : STP 0 -> 0 -1 |
0 2 : STP 0 -> 0 -1 |
||
=={{header|Go}}== |
|||
<syntaxhighlight lang="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*` + // label |
|||
`(NOP|LDA|STA|ADD|SUB|BRZ|JMP|STP)?\s*` + // opcode |
|||
`(\w+)?\s*` + // argument |
|||
`(?:;([\w\s]+))?`) // comment |
|||
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) |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
4 |
|||
56 |
|||
55 |
|||
6 |
|||
1 |
|||
255 |
|||
0 |
|||
</pre> |
|||
=={{header|J}}== |
=={{header|J}}== |