-
Notifications
You must be signed in to change notification settings - Fork 0
/
computer.go
130 lines (114 loc) · 2.39 KB
/
computer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"bufio"
"errors"
"io"
"strconv"
)
// Specifies all the state of a computer
type Computer struct {
Accumulator int
PC int
Halted bool
Memory [100]int
Input io.Reader
Output io.Writer
bufInput *bufio.Reader
}
// Wraps the given int into [0, 999]
func wrap(i int) int {
i += 999
r := i % 1999
if r < 0 {
r += 1999
}
return r - 999
}
// Separates the given int into the opcode and operand
func separate(c int) (int, int) {
if c < 0 || c > 999 {
return -1, 0
}
opcode := c / 100
return opcode, c - opcode*100
}
var (
ErrMachineOff = errors.New("Machine halted")
ErrPCOutOfBounds = errors.New("Program counter out of bounds")
ErrInvalidCommand = errors.New("Invalid command passed to CPU")
ErrInputOutput = errors.New("Error inputting")
)
// Makes a step over the loaded program
func (c *Computer) Step() error {
if c.Halted {
return ErrMachineOff
}
if c.PC < 0 || c.PC > 99 {
return ErrPCOutOfBounds
}
opcode, oprand := separate(c.Memory[c.PC])
switch opcode {
case 1: // ADD
c.Accumulator += c.Memory[oprand]
case 2: // SUBTRACT
c.Accumulator -= c.Memory[oprand]
case 3: // STORE
c.Memory[oprand] = c.Accumulator
case 5: // LOAD
c.Accumulator = c.Memory[oprand]
case 6: // BRANCH
c.PC = oprand - 1
case 7: // BRANCH IF ZERO
if c.Accumulator == 0 {
c.PC = oprand - 1
}
case 8: // BRANCH IF POSITIVE
if c.Accumulator >= 0 {
c.PC = oprand - 1
}
case 9: // INPUT OUTPUT
switch oprand {
case 1: // INPUT
str, err := c.bufInput.ReadString('\n')
if len(str) == 0 || err != nil {
return ErrInputOutput
}
i, err := strconv.Atoi(str[:len(str)-1])
if err != nil {
return ErrInputOutput
}
c.Accumulator = i
case 2: // OUTPUT
c.Output.Write([]byte(strconv.Itoa(c.Accumulator) + "\n"))
}
case 0: // HALT
c.Halted = true
default:
return ErrInvalidCommand
}
c.PC++
if c.PC > 99 {
c.PC = 0
}
c.Memory[oprand] = wrap(c.Memory[oprand])
c.Accumulator = wrap(c.Accumulator)
return nil
}
// Loads the given program into memory and resets registers
func (c *Computer) Load(prog [100]int) error {
for i, v := range prog {
c.Memory[i] = wrap(v)
}
c.Accumulator = 0
c.PC = 0
return nil
}
// Creates and returns a new computer
func NewComputer(in io.Reader, out io.Writer) *Computer {
c := &Computer{
Input: in,
Output: out,
}
c.bufInput = bufio.NewReader(c.Input)
return c
}