lc3spec 0.1.4 → 0.1.5
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 +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
|