lc3spec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []