lc3spec 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -11
- data/Rakefile +6 -0
- data/lib/lc3spec/helpers.rb +6 -2
- data/lib/lc3spec/lc3.rb +148 -55
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f5b656483154d73d7f647eab19d47e71b190eb4
|
4
|
+
data.tar.gz: 047edfaf5379bacca219828cac075a2c7c94dd7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b4128a0a93a20af60494639a03365a6a83e6cd4b8ba034e809494980a06a76ddc8cebf1fcabd8fb03b0c433346602a30ddcf13ba2f902019822edbb238beb24
|
7
|
+
data.tar.gz: 0921354e30fd9779f6bd694e29fccc8d0ab83676dedd92e9c1e97f46e6942bbec44affa380374ed07f7c1ed407a7a1cb1293a9ace383461b3d065428e3d17f56
|
data/README.md
CHANGED
@@ -28,10 +28,6 @@ set registers R0, R1, R2, R3, and R4 to 0, 1, 2, 3, and 4, respectively:
|
|
28
28
|
We can write a spec, spec.rb, to test it:
|
29
29
|
|
30
30
|
```ruby
|
31
|
-
#!/usr/bin/env ruby
|
32
|
-
|
33
|
-
require 'lc3spec'
|
34
|
-
|
35
31
|
test 'Register Expectations' do
|
36
32
|
file 'regs'
|
37
33
|
set_breakpoint 'TRAP_HALT'
|
@@ -45,7 +41,7 @@ test 'Register Expectations' do
|
|
45
41
|
end
|
46
42
|
```
|
47
43
|
|
48
|
-
Running with `
|
44
|
+
Running with `lc3spec spec.rb`:
|
49
45
|
|
50
46
|
```
|
51
47
|
Register Expectations [FAIL]
|
@@ -77,18 +73,14 @@ end
|
|
77
73
|
|
78
74
|
There are currently two supported options:
|
79
75
|
|
80
|
-
*
|
81
|
-
*
|
76
|
+
* `:output` - filename or file to write output to, default is `$stdout`
|
77
|
+
* `:keep_score` - whether or not to display score for each test. If false,
|
82
78
|
only `[OK]` or `[FAIL]` are displayed, if true, a fractional score
|
83
79
|
is displayed, e.g., `0/1` or `4/4`
|
84
80
|
|
85
81
|
An example:
|
86
82
|
|
87
83
|
```ruby
|
88
|
-
#!/usr/bin/env ruby
|
89
|
-
|
90
|
-
require 'lc3spec'
|
91
|
-
|
92
84
|
configure do
|
93
85
|
set :output, 'feedback.txt'
|
94
86
|
set :keep_score, true
|
@@ -137,6 +129,7 @@ gem install lc3spec
|
|
137
129
|
|
138
130
|
* Documentation
|
139
131
|
* Tests
|
132
|
+
* Better expectations, e.g., for arrays or strings in memory
|
140
133
|
|
141
134
|
# Bugs
|
142
135
|
|
data/Rakefile
ADDED
data/lib/lc3spec/helpers.rb
CHANGED
@@ -8,10 +8,12 @@ module LC3Spec
|
|
8
8
|
if number =~ /^(?:x|0x|X|0X)?([0-9A-Fa-f]{1,4})$/
|
9
9
|
"x#{$1.rjust(4, '0').upcase}"
|
10
10
|
else
|
11
|
-
raise "Unable to normalize number: #{number}"
|
11
|
+
raise ArgumentError, "Unable to normalize number: #{number}"
|
12
12
|
end
|
13
13
|
when Fixnum
|
14
14
|
"x#{([number].pack('s>*')).unpack('H*').first.upcase}"
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Expecting String or Fixnum, got #{number.class}"
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
@@ -29,10 +31,12 @@ module LC3Spec
|
|
29
31
|
|
30
32
|
[num_part].pack('H*').unpack('s>*').first
|
31
33
|
else
|
32
|
-
raise "Unable to normalize number: #{number}"
|
34
|
+
raise ArgumentError, "Unable to normalize number: #{number}"
|
33
35
|
end
|
34
36
|
when Fixnum
|
35
37
|
number
|
38
|
+
else
|
39
|
+
raise ArgumentError, "Expecting String or Fixnum, got #{number.class}"
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
data/lib/lc3spec/lc3.rb
CHANGED
@@ -40,20 +40,36 @@ class LC3
|
|
40
40
|
initialize_lc3sim
|
41
41
|
end
|
42
42
|
|
43
|
+
# Get the value of a register
|
44
|
+
#
|
45
|
+
# @param [Symbol] reg the register, one of :R0 through :R7,
|
46
|
+
# :PC, :IR, :PSR, or :CC
|
47
|
+
# @return [String, nil] the register value in hex format (e.g., 'x0000') or
|
48
|
+
# nil if the register does not exist.
|
43
49
|
def get_register(reg)
|
44
50
|
reg = reg.to_s.upcase.to_sym # Ruby 1.8 doesn't support Symbol#upcase
|
45
51
|
@registers[reg]
|
46
52
|
end
|
47
53
|
|
54
|
+
# Set the value of a register
|
55
|
+
#
|
56
|
+
# @param (see #get_register)
|
57
|
+
# @param [String] val the register value in hex format (e.g., 'xF1D0')
|
58
|
+
# @raise [ArgumentError] if the register is invalid or the value is nil,
|
59
|
+
# @return [self]
|
48
60
|
def set_register(reg, val)
|
49
|
-
reg = reg.
|
61
|
+
reg = reg.upcase
|
50
62
|
|
51
63
|
unless @registers.keys.include? reg.to_sym
|
52
|
-
raise "Invalid register: #{reg.to_s}"
|
64
|
+
raise ArgumentError, "Invalid register: #{reg.to_s}"
|
53
65
|
end
|
54
66
|
|
55
67
|
if val.nil?
|
56
|
-
raise "Invalid register value for #{reg.to_s}: #{val}"
|
68
|
+
raise ArgumentError, "Invalid register value for #{reg.to_s}: #{val}"
|
69
|
+
end
|
70
|
+
|
71
|
+
if reg.to_sym == :CC and not ['POSITIVE', 'NEGATIVE', 'ZERO'].include? val
|
72
|
+
raise ArgumentError, "CC can only be set to NEGATIVE, ZERO, or POSITIVE"
|
57
73
|
end
|
58
74
|
|
59
75
|
@io.puts "register #{reg.to_s} #{normalize_to_s(val)}"
|
@@ -62,21 +78,37 @@ class LC3
|
|
62
78
|
msg = @io.readline
|
63
79
|
parse_msg msg.strip
|
64
80
|
|
65
|
-
break if msg =~ /^TOCODE/
|
81
|
+
break if msg =~ /^ERR|TOCODE/
|
66
82
|
end
|
67
83
|
|
68
84
|
self
|
69
85
|
end
|
70
86
|
|
87
|
+
# Return value in memory at the given address or label
|
88
|
+
#
|
89
|
+
# @param [String] addr an address in hex format (e.g., 'xADD4') or a label
|
90
|
+
# @raise [ArgumentError] if the argument is not a existing label or is an
|
91
|
+
# invalid address
|
92
|
+
# @return [String] the value in memory in hex format
|
71
93
|
def get_memory(addr)
|
72
94
|
if addr.respond_to?(:upcase)
|
73
|
-
label_addr = get_address(addr)
|
74
|
-
|
95
|
+
label_addr = get_address(addr.upcase)
|
96
|
+
|
97
|
+
return @memory[label_addr] unless label_addr.nil?
|
75
98
|
end
|
76
99
|
|
77
|
-
@memory[
|
100
|
+
@memory[normalize_to_s(addr)]
|
78
101
|
end
|
79
102
|
|
103
|
+
# Set memory at the given address or label
|
104
|
+
#
|
105
|
+
# If val is a label, mem[addr] will be set to the address of val
|
106
|
+
#
|
107
|
+
# @param [String] addr an address or a label
|
108
|
+
# @param [String] val a value or a label
|
109
|
+
# @raise [ArgumentError] if addr is not an existing label, addr is an
|
110
|
+
# invalid address, val is not an existing label, or val is invalid
|
111
|
+
# @return [self]
|
80
112
|
def set_memory(addr, val)
|
81
113
|
# addr may be memory address or label
|
82
114
|
|
@@ -88,7 +120,15 @@ class LC3
|
|
88
120
|
addr = normalize_to_s(addr)
|
89
121
|
end
|
90
122
|
|
91
|
-
|
123
|
+
# Value can be a label too
|
124
|
+
if val.respond_to?(:upcase) and @labels.include?(val.upcase.to_s)
|
125
|
+
# Is a label
|
126
|
+
else
|
127
|
+
# Is a value
|
128
|
+
val = normalize_to_s(val)
|
129
|
+
end
|
130
|
+
|
131
|
+
@io.puts("memory #{addr} #{val}")
|
92
132
|
|
93
133
|
loop do
|
94
134
|
msg = @io.readline
|
@@ -100,10 +140,21 @@ class LC3
|
|
100
140
|
self
|
101
141
|
end
|
102
142
|
|
143
|
+
# Get the address of a label
|
144
|
+
#
|
145
|
+
# @param [String] label
|
146
|
+
# @return [String, nil] the address of the label, or nil if the label does
|
147
|
+
# not exist
|
103
148
|
def get_address(label)
|
104
149
|
@labels[label.upcase.to_s]
|
105
150
|
end
|
106
151
|
|
152
|
+
# Load a file given a path
|
153
|
+
#
|
154
|
+
# @param [String] filename the path of a file to load into lc3sim. The
|
155
|
+
# argument should have .obj extension or no extension at all, in which
|
156
|
+
# case .obj is assumed
|
157
|
+
# @return [self]
|
107
158
|
def file(filename)
|
108
159
|
@io.puts "file #{filename}"
|
109
160
|
|
@@ -129,6 +180,9 @@ class LC3
|
|
129
180
|
self
|
130
181
|
end
|
131
182
|
|
183
|
+
# Execute one instruction
|
184
|
+
#
|
185
|
+
# @return [self]
|
132
186
|
def step
|
133
187
|
@io.puts 'step'
|
134
188
|
|
@@ -137,6 +191,9 @@ class LC3
|
|
137
191
|
self
|
138
192
|
end
|
139
193
|
|
194
|
+
# Execute instructions until halt or breakpoint
|
195
|
+
#
|
196
|
+
# @return [self]
|
140
197
|
def continue
|
141
198
|
@io.puts 'continue'
|
142
199
|
|
@@ -145,6 +202,11 @@ class LC3
|
|
145
202
|
self
|
146
203
|
end
|
147
204
|
|
205
|
+
# Set a breakpoint at an address or label
|
206
|
+
#
|
207
|
+
# @param (see #get_memory)
|
208
|
+
# @raise (see #get_memory)
|
209
|
+
# @return [self]
|
148
210
|
def set_breakpoint(addr)
|
149
211
|
addr = addr.upcase.to_s if addr.respond_to? :upcase
|
150
212
|
if @labels.include? addr
|
@@ -163,6 +225,11 @@ class LC3
|
|
163
225
|
self
|
164
226
|
end
|
165
227
|
|
228
|
+
# Clear a breakpoint at an address or label
|
229
|
+
#
|
230
|
+
# @param (see #set_breakpoint)
|
231
|
+
# @raise (see #set_breakpoint)
|
232
|
+
# @return [self]
|
166
233
|
def clear_breakpoint(addr)
|
167
234
|
if addr == :all
|
168
235
|
@io.puts 'break clear all'
|
@@ -187,30 +254,84 @@ class LC3
|
|
187
254
|
self
|
188
255
|
end
|
189
256
|
|
257
|
+
# Clear all breakpoints
|
258
|
+
#
|
259
|
+
# @return [self]
|
190
260
|
def clear_breakpoints
|
191
261
|
clear_breakpoint :all
|
192
262
|
end
|
193
263
|
|
264
|
+
# Return output from the LC-3
|
265
|
+
#
|
266
|
+
# The LC-3 welcome messages and halt messages are stripped from the output,
|
267
|
+
# so that it can easily compared to expected output.
|
268
|
+
#
|
269
|
+
# WARNING: This is potentially buggy as there is no signal from lc3sim when
|
270
|
+
# the output is ready, so this function just waits until output appears and
|
271
|
+
# reads what is available. This may not work if the output is very long.
|
272
|
+
#
|
273
|
+
# @return [String] the output from the LC-3, or an empty string
|
274
|
+
# if there is no output
|
194
275
|
def get_output
|
195
276
|
out = ''
|
196
277
|
|
278
|
+
# Do something to wait for output to be ready
|
279
|
+
set_register(:PSR, @registers[:PSR])
|
280
|
+
|
197
281
|
# There is no signal that tells the GUI that output is ready...
|
198
282
|
# FIXME: This is a bug waiting to happen
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
283
|
+
catch(:done) do
|
284
|
+
loop do
|
285
|
+
retries = 5
|
286
|
+
until @output.ready?
|
287
|
+
sleep(0.1)
|
288
|
+
|
289
|
+
retries -= 1
|
290
|
+
throw :done if retries <= 0
|
291
|
+
end
|
292
|
+
|
293
|
+
while @output.ready?
|
294
|
+
out << @output.readpartial(1024)
|
295
|
+
end
|
296
|
+
end
|
209
297
|
end
|
210
298
|
|
211
299
|
out.gsub("\n\n--- halting the LC-3 ---\n\n", '')
|
212
300
|
end
|
213
301
|
|
302
|
+
# Close open file descriptors for communicating with lc3sim
|
303
|
+
#
|
304
|
+
# @return [nil]
|
305
|
+
def close
|
306
|
+
@io.close
|
307
|
+
@output.close
|
308
|
+
@server.close
|
309
|
+
end
|
310
|
+
|
311
|
+
# Return a string containing a human-readable representation of the current
|
312
|
+
# state of the LC-3
|
313
|
+
#
|
314
|
+
# @return [String]
|
315
|
+
def inspect
|
316
|
+
registers = @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
|
317
|
+
|
318
|
+
addr_to_label = @labels.invert
|
319
|
+
|
320
|
+
memory_header = "%18s ADDR VALUE" % "label"
|
321
|
+
memory = @memory.map do |addr, value|
|
322
|
+
"%18s %s %s" % [(addr_to_label[addr] or ''), addr, value]
|
323
|
+
end.join("\n")
|
324
|
+
|
325
|
+
#[memory_header, memory, registers].join("\n")
|
326
|
+
registers
|
327
|
+
end
|
328
|
+
|
329
|
+
# def to_s
|
330
|
+
# @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
|
331
|
+
# end
|
332
|
+
|
333
|
+
private
|
334
|
+
|
214
335
|
def initialize_lc3sim
|
215
336
|
# Start lc3sim instance
|
216
337
|
@io = IO.popen(%w(lc3sim -gui), 'r+')
|
@@ -244,32 +365,6 @@ class LC3
|
|
244
365
|
end
|
245
366
|
end
|
246
367
|
|
247
|
-
def close
|
248
|
-
@output.close
|
249
|
-
@server.close
|
250
|
-
end
|
251
|
-
|
252
|
-
def inspect
|
253
|
-
registers = @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
|
254
|
-
|
255
|
-
addr_to_label = @labels.invert
|
256
|
-
|
257
|
-
memory_header = "%18s ADDR VALUE" % "label"
|
258
|
-
memory = @memory.map do |addr, value|
|
259
|
-
"%18s %s %s" % [(addr_to_label[addr] or ''),
|
260
|
-
normalize_to_s(addr), value]
|
261
|
-
end.join("\n")
|
262
|
-
|
263
|
-
#[memory_header, memory, registers].join("\n")
|
264
|
-
registers
|
265
|
-
end
|
266
|
-
|
267
|
-
# def to_s
|
268
|
-
# @registers.map { |k, v| "#{k}=#{v}" }.join(' ')
|
269
|
-
# end
|
270
|
-
|
271
|
-
private
|
272
|
-
|
273
368
|
def parse_until_print_registers
|
274
369
|
loop do
|
275
370
|
msg = @io.readline
|
@@ -296,23 +391,21 @@ class LC3
|
|
296
391
|
when 'BCLEAR'
|
297
392
|
when 'BREAK'
|
298
393
|
when 'CODE', 'CODEP' # Line of code
|
299
|
-
#
|
300
|
-
|
394
|
+
# Numeric address
|
395
|
+
num_addr = tokens.shift.to_i - 1
|
301
396
|
|
302
397
|
# Note: if there is a breakpoint at the current address, the last
|
303
|
-
# token.shift produces a string like "12289B". Luckily,
|
304
|
-
#
|
398
|
+
# token.shift produces a string like "12289B". Luckily, to_i just
|
399
|
+
# reads whatever it can and discards the rest.
|
305
400
|
|
306
401
|
# Label, if present
|
307
|
-
|
308
|
-
label = tokens.shift.upcase
|
402
|
+
label = tokens.first != "x%04X" % num_addr ? tokens.shift.upcase : nil
|
309
403
|
|
310
|
-
|
311
|
-
|
312
|
-
end
|
404
|
+
# hex address
|
405
|
+
addr = tokens.shift
|
313
406
|
|
314
|
-
#
|
315
|
-
|
407
|
+
# Insert label, but don't overwrite if it already exists
|
408
|
+
@labels[label] ||= addr if not label.nil?
|
316
409
|
|
317
410
|
# Value
|
318
411
|
val = tokens.shift
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lc3spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chun Yang
|
@@ -9,7 +9,21 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2013-02-27 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
13
27
|
description: DSL for testing LC-3 assembly programs
|
14
28
|
email: x@cyang.info
|
15
29
|
executables:
|
@@ -30,6 +44,7 @@ files:
|
|
30
44
|
- bin/lc3spec
|
31
45
|
- LICENSE
|
32
46
|
- README.md
|
47
|
+
- Rakefile
|
33
48
|
homepage: http://github.com/chunyang/lc3spec
|
34
49
|
licenses:
|
35
50
|
- MIT
|