lc3spec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d874750aeeb6b7c7659d5c68945fdf7d062d97f
4
+ data.tar.gz: e112c5c35204cf1e18567aaece03b4e9cc18a19a
5
+ SHA512:
6
+ metadata.gz: 3589a767df9570d047bd4d7927d5cfdd78cdcf23017ec3f16e13b81366197f4d2479f247cc8a6eb46f8a97fdc693f7e1d18f6939f67c894d24fe245d7a80bdc1
7
+ data.tar.gz: deab9fada997b3b30cd4bbe5f8a5ea9efdd7e0fe2599899c1a9d2a1ee2c918196d45b85d7d648389466b96b7d0a8e90d4c2cd1eba3c125c5d1e7ece44442684e
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Chun Yang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # LC3Spec
2
+
3
+ Testing and grading suite for LC-3 assembly programs
4
+
5
+ # Requirements
6
+
7
+ * [lc3tools for unix](http://highered.mcgraw-hill.com/sites/0072467509/student_view0/lc-3_simulator.html)
@@ -0,0 +1,207 @@
1
+ require 'open3'
2
+ require 'timeout'
3
+
4
+ require 'lc3spec/constants'
5
+ require 'lc3spec/errors'
6
+ require 'lc3spec/expectations'
7
+ require 'lc3spec/lc3'
8
+ require 'lc3spec/helpers'
9
+ require 'lc3spec/reporter'
10
+
11
+ module LC3Spec
12
+ class Test
13
+ include LC3Spec::Helpers
14
+
15
+ attr_accessor :pass, :reporter
16
+
17
+ def initialize(options, &block)
18
+ # Save src_dir
19
+ @src_dir = File.expand_path(Dir.pwd)
20
+ @reporter = Reporter.new
21
+ @pass = true
22
+
23
+ # Do everything inside tmp_dir
24
+ Dir.mktmpdir('spec') do |tmp_dir|
25
+ Dir.chdir(tmp_dir) do
26
+ @lc3 = LC3.new
27
+
28
+ begin
29
+ instance_eval(&block) if block_given?
30
+ rescue Timeout::Error => err
31
+ @reporter.report "Execution timed-out, likely due to an infinite loop"
32
+ rescue LC3Spec::DoesNotAssembleError => err
33
+ @reporter.report err.message
34
+ $stderr.puts err.message
35
+ end
36
+
37
+ @pass = false if @reporter.fail?
38
+ end
39
+ end
40
+ end
41
+
42
+ def set_register(reg, val = 0)
43
+ case reg
44
+ when Hash
45
+ reg.each do |r, v|
46
+ @lc3.set_register(r, v)
47
+ end
48
+ when Array
49
+ reg.each_with_index do |v, i|
50
+ @lc3.set_register("R#{i}", v)
51
+ end
52
+ else
53
+ @lc3.set_register(reg, val)
54
+ end
55
+ end
56
+
57
+ alias_method :set_registers, :set_register
58
+
59
+ def file_from_asm(asm)
60
+ assemble_and_load { |f| f.puts asm }
61
+
62
+ self
63
+ end
64
+
65
+ def file(filename)
66
+ ensure_present(filename)
67
+ ensure_assembled(filename)
68
+ @lc3.file(filename)
69
+
70
+ self
71
+ end
72
+
73
+ def continue(duration = 1.5)
74
+ Timeout.timeout(duration) do
75
+ @lc3.continue
76
+ end
77
+ end
78
+
79
+ alias_method :load_file, :file
80
+
81
+ def set_label(label, addr)
82
+ label = label.to_s.upcase
83
+
84
+ if @labels.include? label
85
+ raise "Unable to replace label #{label}"
86
+ end
87
+
88
+ assemble_and_load do |f|
89
+ f.puts ".ORIG #{normalize_to_s(addr)}\n#{label}\n.END"
90
+ end
91
+
92
+ self
93
+ end
94
+
95
+ def method_missing(method_name, *arguments, &block)
96
+ if @lc3.respond_to? method_name
97
+ @lc3.send(method_name, *arguments, &block)
98
+
99
+ self
100
+ elsif method_name.to_s =~ /^expect_/
101
+ LC3Spec::Expectations.send(method_name, @lc3, @reporter, *arguments, &block)
102
+
103
+ self
104
+ else
105
+ super
106
+ end
107
+ end
108
+
109
+ def respond_to_missing?(method_name, include_private = false)
110
+ if @lc3.respond_to? method_name
111
+ true
112
+ elsif method_name.to_s =~ /^expect_/
113
+ true
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def assemble_and_load(name = 'lc3spec-tmp', &block)
122
+ return nil unless block_given?
123
+
124
+ f = Tempfile.new([name, '.asm'])
125
+ begin
126
+ yield f
127
+ f.close
128
+
129
+ ensure_assembled(f.path)
130
+ prefix = f.path.chomp(File.extname(f.path))
131
+ @lc3.file(prefix)
132
+
133
+ ensure
134
+ f.unlink
135
+
136
+ # Try not to accidentally delete all files....
137
+ unless prefix.nil? or prefix.empty?
138
+ Dir.glob(prefix + '*') do |filename|
139
+ File.unlink filename
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ def ensure_present(filename)
146
+ if filename.nil? or filename.empty?
147
+ raise DoesNotAssembleError, 'Filename nil or empty'
148
+ end
149
+
150
+ # Copy file to current directory if needed
151
+ basename = File.basename filename
152
+
153
+ if Dir.glob(basename + '*').empty?
154
+ if is_absolute_path?(filename)
155
+ Dir.glob(filename + '') do |fn|
156
+ FileUtils.cp(fn, '.')
157
+ end
158
+ else
159
+ Dir.glob(File.join(@src_dir, filename + '*')) do |fn|
160
+ FileUtils.cp(fn, '.')
161
+ end
162
+ end
163
+ end
164
+
165
+ # Check if we have the file now
166
+ if Dir.glob(basename + '*').empty?
167
+ raise DoesNotAssembleError, "Cannot find file #{basename}"
168
+ end
169
+ end
170
+
171
+ def ensure_assembled(filename)
172
+ if filename !~ /\.asm$/ and filename !~ /\.obj/
173
+ asm_file = filename + '.asm'
174
+ obj_file = filename + '.obj'
175
+
176
+ # Always prefer finding .asm over .obj file
177
+ if File.exist? asm_file
178
+ filename = asm_file
179
+ elsif File.exist? obj_file
180
+ filename = obj_file
181
+ else
182
+ raise DoesNotAssembleError, "Cannot find #{asm_file} or #{obj_file}"
183
+ end
184
+ end
185
+
186
+ # Check that the file assembles
187
+ case filename
188
+ when /\.asm$/
189
+ error, status = Open3.capture2e('lc3as', filename)
190
+ if not status.success?
191
+ raise DoesNotAssembleError,
192
+ "File does not assemble: #{filename}\n#{error}"
193
+ end
194
+ when /\.obj$/
195
+ if File.size? filename
196
+ success = true
197
+ else
198
+ raise DoesNotAssembleError,
199
+ "File has zero size or does not exist: #{filename}"
200
+ end
201
+ end
202
+
203
+ return true
204
+ end
205
+ end
206
+
207
+ end
@@ -0,0 +1,17 @@
1
+ module LC3Spec
2
+ module Constants
3
+ R0 = :R0
4
+ R1 = :R1
5
+ R2 = :R2
6
+ R3 = :R3
7
+ R4 = :R4
8
+ R5 = :R5
9
+ R6 = :R6
10
+ R7 = :R7
11
+
12
+ PC = :PC
13
+ IR = :IR
14
+ PSR = :PSR
15
+ CC = :CC
16
+ end
17
+ end
@@ -0,0 +1,108 @@
1
+ module LC3Spec
2
+ module Dsl
3
+ def set(option, value = true)
4
+ @options ||= {
5
+ :output => $stdout,
6
+ :keep_score => false
7
+ }
8
+
9
+ if option == :output
10
+ case value
11
+ when File
12
+ if value.path =~ /\.asm$/
13
+ raise "Not writing output to .asm file"
14
+ end
15
+
16
+ @options[:output] = value
17
+ when String
18
+ if value =~ /\.asm$/
19
+ raise "Not writing output to .asm file"
20
+ end
21
+
22
+ @options[:output] = open(value, 'w')
23
+ else
24
+ @options[:output] = value
25
+ end
26
+
27
+ return @options[:output]
28
+ end
29
+
30
+ @options[option] = value
31
+
32
+ end
33
+
34
+ def configure(&block)
35
+ yield if block_given?
36
+ end
37
+
38
+ def test(description, points = 0, &block)
39
+ t = LC3Spec::Test.new(:options => @options,
40
+ :points => points,
41
+ &block)
42
+
43
+ output = @options[:output]
44
+
45
+ if t.pass
46
+ pass(description, points)
47
+ else
48
+ fail(description, points)
49
+
50
+ # FIXME: Ugly
51
+ errors = t.reporter.errors.join("\n")
52
+
53
+ output.puts errors.each_line.map { |line| ' ' + line }.join('')
54
+ output.puts
55
+ end
56
+ end
57
+
58
+ def pass(description, points)
59
+ count_points(:pass, points)
60
+
61
+ if !@options[:keep_score] || (points == 0)
62
+ @options[:output].puts "#{description} [OK]"
63
+ else
64
+ @options[:output].puts "#{description} #{points}/#{points}"
65
+ end
66
+ end
67
+
68
+ def fail(description, points)
69
+ count_points(:fail, points)
70
+
71
+ if !@options[:keep_score] || (points == 0)
72
+ @options[:output].puts "#{description} [FAIL]"
73
+ else
74
+ @options[:output].puts "#{description} 0/#{points}"
75
+ end
76
+ end
77
+
78
+ def count_points(result, possible)
79
+ @possible_points ||= 0
80
+ @earned_points ||= 0
81
+
82
+ @earned_points += result == :pass ? possible : 0
83
+ @possible_points += possible
84
+
85
+ @num_tests ||= 0
86
+ @num_tests += 1
87
+
88
+ @num_passed ||= 0
89
+ @num_passed += result == :pass ? 1 : 0
90
+ end
91
+
92
+ def print_score
93
+ return if @num_tests.nil? or (@num_tests == 0)
94
+
95
+ output = @options[:output]
96
+
97
+ if @options[:keep_score]
98
+ output.puts "Score: #@earned_points/#@possible_points"
99
+ else
100
+ if @num_passed == @num_tests
101
+ output.puts "[ALL OK]"
102
+ elsif @num_passed == 0
103
+ output.puts "[ALL FAIL]"
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,5 @@
1
+ module LC3Spec
2
+ class LC3Error < StandardError; end
3
+ class DoesNotAssembleError < LC3Error; end
4
+ class ExpectationNotMetError < LC3Error; end
5
+ end
@@ -0,0 +1,68 @@
1
+ require 'lc3spec/errors'
2
+ require 'lc3spec/helpers'
3
+
4
+ module LC3Spec
5
+ module Expectations
6
+ extend LC3Spec::Helpers
7
+
8
+ def self.expect_register(lc3, reporter, reg, val)
9
+ expected = normalize_to_s(val)
10
+ actual = lc3.get_register(reg)
11
+
12
+ unless expected == actual
13
+ reporter.report "Incorrect #{reg.to_s}: #{diff(expected, actual)}"
14
+ end
15
+ end
16
+
17
+ def self.expect_memory(lc3, reporter, addr, val)
18
+ expected = normalize_to_s(val)
19
+ actual = lc3.get_memory(addr)
20
+
21
+ unless expected == actual
22
+ reporter.report "Incorrect mem[#{addr}]: #{diff(expected, actual)}"
23
+ end
24
+ end
25
+
26
+ def self.expect_output(lc3, reporter, expected)
27
+ actual = lc3.get_output
28
+
29
+ unless ignore_whitespace_equal(expected, actual)
30
+ reporter.report "Incorrect output:\n" +
31
+ " expected:\n#{strblock(expected)}\n actual:\n#{strblock(actual)}"
32
+ end
33
+ end
34
+
35
+ def self.diff(expected, actual)
36
+ "expected: #{expected}, actual: #{actual}"
37
+ end
38
+
39
+ def self.strblock(str, indent = ' ')
40
+ return str if str.nil? or str.empty?
41
+ block = ''
42
+ str.each_line do |line|
43
+ block << indent + line
44
+ end
45
+ block
46
+ end
47
+
48
+ def self.ignore_whitespace_equal(lhs, rhs)
49
+ lhs = lhs.each_line.map do |line|
50
+ if line =~ /^\s*$/
51
+ nil
52
+ else
53
+ line.gsub(/[ \t]+/, ' ')
54
+ end
55
+ end.compact.join('').strip
56
+
57
+ rhs = rhs.each_line.map do |line|
58
+ if line =~ /^\s*$/
59
+ nil
60
+ else
61
+ line.gsub(/[ \t]+/, ' ')
62
+ end
63
+ end.compact.join('').strip
64
+
65
+ lhs == rhs
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,43 @@
1
+ require 'pathname'
2
+
3
+ module LC3Spec
4
+ module Helpers
5
+ def normalize_to_s(number)
6
+ case number
7
+ when String
8
+ if number =~ /^(?:x|0x|X|0X)?([0-9A-Fa-f]{1,4})$/
9
+ "x#{$1.rjust(4, '0').upcase}"
10
+ else
11
+ raise "Unable to normalize number: #{number}"
12
+ end
13
+ when Fixnum
14
+ "x#{([number].pack('s>*')).unpack('H*').first.upcase}"
15
+ end
16
+ end
17
+
18
+ def normalize_to_i(number)
19
+ case number
20
+ when String
21
+ if number =~ /^(?:x|0x|X|0X)?([0-9A-Fa-f]{1,4})$/
22
+ num_part = $1
23
+
24
+ if num_part[0].to_i(16) > 7
25
+ num_part = num_part.rjust(4, 'F')
26
+ else
27
+ num_part = num_part.rjust(4, '0')
28
+ end
29
+
30
+ [num_part].pack('H*').unpack('s>*').first
31
+ else
32
+ raise "Unable to normalize number: #{number}"
33
+ end
34
+ when Fixnum
35
+ number
36
+ end
37
+ end
38
+
39
+ def is_absolute_path?(path)
40
+ Pathname.new(path).absolute?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,352 @@
1
+ # lc3.rb
2
+ # Chun Yang <yang43@illinois.edu>
3
+
4
+ require 'io/wait'
5
+ require 'logger'
6
+ require 'socket'
7
+ require 'tempfile'
8
+
9
+ require 'lc3spec/errors'
10
+ require 'lc3spec/helpers'
11
+
12
+ # Class to provide access to LC-3 simulator instance
13
+ class LC3
14
+ include LC3Spec::Helpers
15
+ attr_accessor :logger
16
+
17
+ def initialize
18
+ # Logging
19
+ @logger = Logger.new(STDERR)
20
+ @logger.level = Logger::ERROR
21
+ #@logger.level = Logger::DEBUG
22
+ @logger.formatter = proc do |severity, datetime, progname, msg|
23
+ "#{severity}: #{msg}\n"
24
+ end
25
+
26
+ # Registers
27
+ @registers = {}
28
+ [:R0, :R1, :R2, :R3, :R4, :R5, :R6, :R7, :PC, :IR, :PSR].each do |reg|
29
+ @registers[reg] = 'x0000'
30
+ end
31
+ @registers[:CC] = 'ZERO'
32
+
33
+ # Memory
34
+ @memory = Hash.new('x0000')
35
+ @labels = {}
36
+
37
+ initialize_lc3sim
38
+ end
39
+
40
+ def get_register(reg)
41
+ @registers[reg]
42
+ end
43
+
44
+ def set_register(reg, val)
45
+ reg = reg.upcase # Don't use ! version because it doesn't work for symbols
46
+
47
+ unless @registers.keys.include? reg.to_sym
48
+ raise "Invalid register: #{reg.to_s}"
49
+ end
50
+
51
+ if val.nil?
52
+ raise "Invalid register value for #{reg.to_s}: #{val}"
53
+ end
54
+
55
+ @io.puts "register #{reg.to_s} #{normalize_to_s(val)}"
56
+
57
+ loop do
58
+ msg = @io.readline
59
+ parse_msg msg.strip
60
+
61
+ break if msg =~ /^TOCODE/
62
+ end
63
+
64
+ self
65
+ end
66
+
67
+ def get_memory(addr)
68
+ if addr.respond_to?(:upcase)
69
+ label_addr = get_address(addr)
70
+ addr = label_addr unless label_addr.nil?
71
+ end
72
+
73
+ @memory[normalize_to_i(addr)]
74
+ end
75
+
76
+ def set_memory(addr, val)
77
+ # addr may be memory address or label
78
+
79
+ if addr.respond_to?(:upcase) and @labels.include?(addr.upcase.to_s)
80
+ # Is a label
81
+ addr = addr.upcase.to_s
82
+ else
83
+ # Not a label
84
+ addr = normalize_to_s(addr)
85
+ end
86
+
87
+ @io.puts("memory #{addr} #{normalize_to_s(val)}")
88
+
89
+ loop do
90
+ msg = @io.readline
91
+ parse_msg msg.strip
92
+
93
+ break if msg =~ /^ERR|CODE/
94
+ end
95
+
96
+ self
97
+ end
98
+
99
+ def get_address(label)
100
+ @labels[label.upcase.to_s]
101
+ end
102
+
103
+ def file(filename)
104
+ @io.puts "file #{filename}"
105
+
106
+ # Need to encounter 2 TOCODEs before we're done
107
+ tocode_counter = 2
108
+
109
+ while tocode_counter > 0
110
+ msg = @io.readline
111
+
112
+ parse_msg msg.strip
113
+
114
+ if msg =~ /^TOCODE/
115
+ tocode_counter -= 1
116
+ end
117
+
118
+ # ignore warning about no symbols
119
+ next if msg =~ /WARNING: No symbols/
120
+
121
+ # don't ignore other errors
122
+ break if msg =~ /^ERR/
123
+ end
124
+
125
+ self
126
+ end
127
+
128
+ def step
129
+ @io.puts 'step'
130
+
131
+ parse_until_print_registers
132
+
133
+ self
134
+ end
135
+
136
+ def continue
137
+ @io.puts 'continue'
138
+
139
+ parse_until_print_registers
140
+
141
+ self
142
+ end
143
+
144
+ def set_breakpoint(addr)
145
+ addr = addr.upcase.to_s if addr.respond_to? :upcase
146
+ if @labels.include? addr
147
+ @io.puts "break set #{addr}"
148
+ else
149
+ @io.puts "break set #{normalize_to_s(addr)}"
150
+ end
151
+
152
+ sleep(0.01)
153
+
154
+ while @io.ready?
155
+ msg = @io.readline
156
+ parse_msg msg.strip
157
+ end
158
+
159
+ self
160
+ end
161
+
162
+ def clear_breakpoint(addr)
163
+ if addr == :all
164
+ @io.puts 'break clear all'
165
+ return self
166
+ end
167
+
168
+ addr = addr.upcase.to_s if addr.respond_to? :upcase
169
+
170
+ if @labels.include? addr
171
+ @io.puts "break clear #{addr}"
172
+ else
173
+ @io.puts "break clear #{normalize_to_s(addr)}"
174
+ end
175
+
176
+ sleep(0.01)
177
+
178
+ while @io.ready?
179
+ msg = @io.readline
180
+ parse_msg msg.strip
181
+ end
182
+
183
+ self
184
+ end
185
+
186
+ def clear_breakpoints
187
+ clear_breakpoint :all
188
+ end
189
+
190
+ def get_output
191
+ out = ''
192
+
193
+ # There is no signal that tells the GUI that output is ready...
194
+ # FIXME: This is a bug waiting to happen
195
+ until @output.ready?
196
+ sleep(0.01)
197
+ end
198
+
199
+ while @output.ready?
200
+ out << @output.readpartial(1024)
201
+ end
202
+
203
+ out.gsub("\n\n--- halting the LC-3 ---\n\n", '')
204
+ end
205
+
206
+ def initialize_lc3sim
207
+ # Start lc3sim instance
208
+ @io = IO.popen(%w(lc3sim -gui), 'r+')
209
+
210
+ begin
211
+ # Port for output server
212
+ @port = (rand * 1000 + 5000).to_i
213
+
214
+ # Start server to get output
215
+ @server = TCPServer.new @port
216
+ rescue Errno::EADDRINUSE
217
+ retry
218
+ end
219
+
220
+ th = Thread.new do
221
+ @output = @server.accept
222
+ end
223
+
224
+ # Initialize lc3sim with port number
225
+ @io.puts @port
226
+
227
+ # Wait for lc3sim to connect with our server
228
+ th.join
229
+
230
+ # Read LC-3 initialization messages (mostly loading lc3os)
231
+ parse_until_print_registers
232
+
233
+ # Clear all the welcome messages from lc3sim output
234
+ while @output.ready?
235
+ @output.readpartial 1024
236
+ end
237
+ end
238
+
239
+ def close
240
+ @output.close
241
+ @server.close
242
+ end
243
+
244
+ def inspect
245
+ registers = @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
246
+
247
+ addr_to_label = @labels.invert
248
+
249
+ memory_header = "%18s ADDR VALUE" % "label"
250
+ memory = @memory.map do |addr, value|
251
+ "%18s %s %s" % [(addr_to_label[addr] or ''),
252
+ normalize_to_s(addr), value]
253
+ end.join("\n")
254
+
255
+ #[memory_header, memory, registers].join("\n")
256
+ registers
257
+ end
258
+
259
+ # def to_s
260
+ # @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
261
+ # end
262
+
263
+ private
264
+
265
+ def parse_until_print_registers
266
+ loop do
267
+ msg = @io.readline
268
+ parse_msg msg.strip
269
+
270
+ break if msg =~ /^REG R11/
271
+ end
272
+ end
273
+
274
+ # Parse messages from lc3sim
275
+ def parse_msg(msg)
276
+ @logger.debug msg
277
+
278
+ tokens = msg.split(/ +/)
279
+
280
+ cmd = tokens.shift
281
+
282
+ if cmd =~ /^CODEP/
283
+ tokens.unshift(cmd.gsub('CODEP', ''))
284
+ cmd = 'CODEP'
285
+ end
286
+
287
+ case cmd
288
+ when 'BCLEAR'
289
+ when 'BREAK'
290
+ when 'CODE', 'CODEP' # Line of code
291
+ # Address
292
+ addr = tokens.shift.to_i - 1
293
+
294
+ # Note: if there is a breakpoint at the current address, the last
295
+ # token.shift produces a string like "12289B". Luckily, #to_i just
296
+ # ignores the B at the end.
297
+
298
+ # Label, if present
299
+ if tokens.first != "x%04X" % addr
300
+ label = tokens.shift.upcase
301
+
302
+ # Don't overwrite label if one already exists
303
+ @labels[label] ||= addr
304
+ end
305
+
306
+ # Discard hex address
307
+ tokens.shift
308
+
309
+ # Value
310
+ val = tokens.shift
311
+
312
+ @memory[addr] = val
313
+
314
+ # Disassembly info
315
+ # info = tokens.join(' ')
316
+
317
+ when 'CONT'
318
+ when 'ERR'
319
+ if msg =~ /WARNING/
320
+ @logger.warn msg
321
+ else
322
+ @logger.error msg
323
+ end
324
+ when 'REG' # Register value
325
+ # Register number
326
+ reg = tokens.shift
327
+
328
+ # Register value
329
+ val = tokens.shift
330
+
331
+ case reg
332
+ when /^R[0-7]$/
333
+ @registers[reg.to_sym] = val
334
+ when 'R8'
335
+ @registers[:PC] = val
336
+ when 'R9'
337
+ @registers[:IR] = val
338
+ when 'R10'
339
+ @registers[:PSR] = val
340
+ when 'R11'
341
+ @registers[:CC] = val
342
+ end
343
+ when 'TOCODE'
344
+ when 'TRANS' # Label address translation
345
+ addr = normalize_to_i(tokens.shift)
346
+ val = tokens.shift
347
+ else
348
+ @logger.debug "Unexpected message: #{msg}"
349
+ end
350
+ end
351
+
352
+ end
@@ -0,0 +1,11 @@
1
+ require 'lc3spec/base'
2
+ require 'lc3spec/dsl'
3
+ require 'lc3spec/constants'
4
+
5
+ #require 'lc3spec/expectations/syntax'
6
+
7
+ extend LC3Spec::Dsl
8
+ include LC3Spec::Constants
9
+
10
+ #include LC3Spec::Expectations::Syntax
11
+ #LC3Spec::Expectations::Syntax.enable_should
@@ -0,0 +1,23 @@
1
+ module LC3Spec
2
+ class Reporter
3
+ def initialize
4
+ @reports = []
5
+ end
6
+
7
+ def report(msg)
8
+ @reports << msg
9
+ end
10
+
11
+ def pass?
12
+ @reports.empty?
13
+ end
14
+
15
+ def fail?
16
+ not @reports.empty?
17
+ end
18
+
19
+ def errors
20
+ @reports.dup
21
+ end
22
+ end
23
+ end
data/lib/lc3spec.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'lc3spec/base'
2
+ require 'lc3spec/main'
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lc3spec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chun Yang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-02-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: DSL for testing LC-3 assembly programs
14
+ email: x@cyang.info
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/lc3spec.rb
20
+ - lib/lc3spec/base.rb
21
+ - lib/lc3spec/constants.rb
22
+ - lib/lc3spec/dsl.rb
23
+ - lib/lc3spec/errors.rb
24
+ - lib/lc3spec/expectations.rb
25
+ - lib/lc3spec/helpers.rb
26
+ - lib/lc3spec/lc3.rb
27
+ - lib/lc3spec/main.rb
28
+ - lib/lc3spec/reporter.rb
29
+ - LICENSE
30
+ - README.md
31
+ homepage: http://github.com/chunyang/lc3spec
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.0.0
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Testing and grading suite for LC-3 assembly programs
55
+ test_files: []