-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
146 lines (118 loc) · 2.67 KB
/
main.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"bytes"
"encoding/binary"
"flag"
"log"
"math"
"os"
"runtime/pprof"
"github.com/nsf/termbox-go"
)
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
// parse flags
debugPtr := flag.Bool("debug", false, "enable debug mode")
cpuProfile := flag.String("cpuprofile", "", "write cpu profile to `file`")
flag.Parse()
// enable the profiler
if *cpuProfile != "" {
f, err := os.Create(*cpuProfile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
// load the program file
path := getPath()
if len(path) == 0 {
log.Fatalln("No program file specified or found")
}
log.Printf("Loading Program: %s", path)
// read the program file into a buffer
mem, err := RetrieveROM(path)
if err != nil {
panic(err)
}
// init the CPU
log.Println("Boot VM")
termbox.Flush()
cpu := NewCPU()
if *debugPtr {
log.Printf("Enabling debug mode")
cpu.DebugMode = true
}
// init memory
cpu.Memory = mem
// init the input loop
go processInput(cpu)
// reset the CPU and start execution
cpu.Reset()
cpu.Run()
log.Println("Terminating VM")
}
func getPath() string {
var arg string
args := flag.Args()
if len(args) > 0 {
arg = args[0]
}
info, err := os.Stat(arg)
if err != nil {
return ""
}
if info.IsDir() {
log.Fatalln("A program file must be specified")
}
return arg
}
func RetrieveROM(filename string) ([65536]uint16, error) {
m := [65536]uint16{}
file, err := os.Open(filename)
if err != nil {
return m, err
}
defer file.Close()
stats, statsErr := file.Stat()
if statsErr != nil {
return m, statsErr
}
// Read origin
// The first 16 bits of the program file specify the address in memory where the
// program should start. This address is called the origin.
var origin uint16
headerBytes := make([]byte, 2)
_, err = file.Read(headerBytes)
if err != nil {
return m, err
}
headerBuffer := bytes.NewBuffer(headerBytes)
// LC-3 programs are big-endian, but most of the modern computers we use are little endian
err = binary.Read(headerBuffer, binary.BigEndian, &origin)
if err != nil {
return m, err
}
log.Printf("Origin memory location: 0x%04X", origin)
var size int64 = stats.Size()
byteArr := make([]byte, size)
log.Printf("Creating memory buffer: %d bytes", size)
_, err = file.Read(byteArr)
if err != nil {
return m, err
}
buffer := bytes.NewBuffer(byteArr)
for i := origin; i < math.MaxUint16; i++ {
var val uint16
binary.Read(buffer, binary.BigEndian, &val)
m[i] = val
}
return m, err
}