rpicsim 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 +7 -0
- data/.yardopts +36 -0
- data/Gemfile +10 -0
- data/Introduction.md +64 -0
- data/LICENSE.txt +24 -0
- data/README.md +66 -0
- data/docs/ChangeLog.md +10 -0
- data/docs/Contributing.md +30 -0
- data/docs/Debugging.md +96 -0
- data/docs/DefiningSimulationClass.md +84 -0
- data/docs/DesignDecisions.md +48 -0
- data/docs/HowMPLABXIsFound.md +14 -0
- data/docs/IntegrationTesting.md +15 -0
- data/docs/IntroductionToRSpec.md +203 -0
- data/docs/IntroductionToRuby.md +90 -0
- data/docs/KnownIssues.md +204 -0
- data/docs/Labels.md +39 -0
- data/docs/MakingTestsRunFaster.md +29 -0
- data/docs/Manual.md +38 -0
- data/docs/PersistentExpectations.md +100 -0
- data/docs/Pins.md +143 -0
- data/docs/PreventingCallStackOverflow.md +94 -0
- data/docs/QuickStartGuide.md +85 -0
- data/docs/RSpecIntegration.md +119 -0
- data/docs/RamWatcher.md +46 -0
- data/docs/Running.md +129 -0
- data/docs/SFRs.md +71 -0
- data/docs/Stubbing.md +161 -0
- data/docs/SupportedCompilers.md +5 -0
- data/docs/SupportedDevices.md +14 -0
- data/docs/SupportedMPLABXVersions.md +12 -0
- data/docs/SupportedOperatingSystems.md +3 -0
- data/docs/UnitTesting.md +9 -0
- data/docs/Variables.md +140 -0
- data/lib/rpicsim.rb +15 -0
- data/lib/rpicsim/call_stack_info.rb +306 -0
- data/lib/rpicsim/flaws.rb +76 -0
- data/lib/rpicsim/instruction.rb +178 -0
- data/lib/rpicsim/label.rb +28 -0
- data/lib/rpicsim/memory.rb +29 -0
- data/lib/rpicsim/memory_watcher.rb +98 -0
- data/lib/rpicsim/mplab.rb +138 -0
- data/lib/rpicsim/mplab/mplab_assembly.rb +102 -0
- data/lib/rpicsim/mplab/mplab_device_info.rb +40 -0
- data/lib/rpicsim/mplab/mplab_disassembler.rb +23 -0
- data/lib/rpicsim/mplab/mplab_instruction.rb +32 -0
- data/lib/rpicsim/mplab/mplab_memory.rb +33 -0
- data/lib/rpicsim/mplab/mplab_nmmr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_observer.rb +12 -0
- data/lib/rpicsim/mplab/mplab_pin.rb +61 -0
- data/lib/rpicsim/mplab/mplab_processor.rb +30 -0
- data/lib/rpicsim/mplab/mplab_program_file.rb +35 -0
- data/lib/rpicsim/mplab/mplab_register.rb +25 -0
- data/lib/rpicsim/mplab/mplab_sfr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_simulator.rb +102 -0
- data/lib/rpicsim/pin.rb +61 -0
- data/lib/rpicsim/program_counter.rb +19 -0
- data/lib/rpicsim/program_file.rb +160 -0
- data/lib/rpicsim/register.rb +78 -0
- data/lib/rpicsim/rspec.rb +11 -0
- data/lib/rpicsim/rspec/be_predicate.rb +12 -0
- data/lib/rpicsim/rspec/helpers.rb +48 -0
- data/lib/rpicsim/rspec/persistent_expectations.rb +53 -0
- data/lib/rpicsim/rspec/sim_diagnostics.rb +51 -0
- data/lib/rpicsim/search.rb +20 -0
- data/lib/rpicsim/sim.rb +702 -0
- data/lib/rpicsim/stack_trace.rb +32 -0
- data/lib/rpicsim/variable.rb +236 -0
- data/lib/rpicsim/version.rb +3 -0
- metadata +114 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
module RPicSim
|
2
|
+
module Flaws
|
3
|
+
# Represents a flaw in RPicSim, usually due to bugs or limitation of the
|
4
|
+
# MPLAB X classes we are using. Stores the name of the flaw and knowledge
|
5
|
+
# about what versions of MPLAB X it affects and how.
|
6
|
+
class Flaw
|
7
|
+
# Creates a new flaw with the specified name.
|
8
|
+
# @param name [Symbol] The name of this flaw.
|
9
|
+
def initialize(name)
|
10
|
+
@versions = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the effect of this flaw on the specified version of MPLAB X.
|
14
|
+
# This might just be true/false to indicate if the flaw is present or it
|
15
|
+
# might be a more complicated thing if there is more than one effect the
|
16
|
+
# the flaw can have.
|
17
|
+
#
|
18
|
+
# @param version [String] A version of MPLAB X, e.g. "1.95".
|
19
|
+
# @return effect
|
20
|
+
def effect(version)
|
21
|
+
if @versions.has_key? version
|
22
|
+
@versions[version]
|
23
|
+
else
|
24
|
+
@probable_affect_for_other_versions
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Records the effect this flaw has on a given version of MPLAB X.
|
29
|
+
def affects_version(version, effect)
|
30
|
+
@versions[version] = effect
|
31
|
+
end
|
32
|
+
|
33
|
+
# Records the effect that this flaw probably has in other versions of
|
34
|
+
# MPLAB X that have not been tested. This allows us to record our guesses
|
35
|
+
# about how the next version of MPLAB X will behave.
|
36
|
+
def probably_affects_other_versions(effect)
|
37
|
+
@probable_affect_for_other_versions = effect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@flaw_hash = {}
|
42
|
+
def self.[](name)
|
43
|
+
@flaw_hash[name].effect Mplab.version
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.add(name)
|
47
|
+
@flaw_hash[name] = flaw = Flaw.new(name)
|
48
|
+
yield flaw
|
49
|
+
end
|
50
|
+
|
51
|
+
add(:fr_memory_attach_useless) do |flaw|
|
52
|
+
flaw.affects_version "1.85", false
|
53
|
+
flaw.affects_version "1.90", false
|
54
|
+
flaw.affects_version "1.95", true
|
55
|
+
flaw.affects_version "2.00", true
|
56
|
+
flaw.probably_affects_other_versions true
|
57
|
+
end
|
58
|
+
|
59
|
+
add(:firmware_cannot_write_user_id0) do |flaw|
|
60
|
+
flaw.affects_version "1.85", true
|
61
|
+
flaw.affects_version "1.90", true
|
62
|
+
flaw.affects_version "1.95", false
|
63
|
+
flaw.affects_version "2.00", false
|
64
|
+
flaw.probably_affects_other_versions false
|
65
|
+
end
|
66
|
+
|
67
|
+
add(:adc_midrange) do |flaw|
|
68
|
+
flaw.affects_version "1.85", :no_middle_values
|
69
|
+
flaw.affects_version "1.90", :bad_modulus
|
70
|
+
flaw.affects_version "1.95", :bad_modulus
|
71
|
+
flaw.affects_version "2.00", :bad_modulus
|
72
|
+
flaw.probably_affects_other_versions :bad_modulus
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module RPicSim
|
2
|
+
# Instances of this class represent a particular instruction at a particular
|
3
|
+
# address in program memory. This class takes low-level information about a
|
4
|
+
# disassembled instruction and produces high-level information about what that
|
5
|
+
# instruction is and how it behaves.
|
6
|
+
#
|
7
|
+
# Instances of this class have links to the other instructions that the instruction
|
8
|
+
# could lead to, so the instances form a graph. This graph is traversed by
|
9
|
+
# classes like {CallStackInfo} to get useful information about the firmware.
|
10
|
+
class Instruction
|
11
|
+
include Comparable
|
12
|
+
|
13
|
+
# The flash address of the instruction.
|
14
|
+
# For PIC18s this will be the byte address.
|
15
|
+
# For other PIC architectures, it will be the word address.
|
16
|
+
# @return (Integer)
|
17
|
+
attr_reader :address
|
18
|
+
|
19
|
+
# The opcode as a capitalized string (e.g. "MOVLW").
|
20
|
+
# @return (String)
|
21
|
+
attr_reader :opcode
|
22
|
+
|
23
|
+
# The operands of the instruction as a hash like { "k" => 92 }.
|
24
|
+
# @return (Hash)
|
25
|
+
attr_reader :operands
|
26
|
+
|
27
|
+
# The number of flash address units that this instruction takes.
|
28
|
+
# The units of this are the same as the units of {#address}.
|
29
|
+
# @return (Integer)
|
30
|
+
attr_reader :size
|
31
|
+
|
32
|
+
# Creates a new instruction.
|
33
|
+
# @param instruction_store some object such as {ProgramFile} that responds to #instruction and #address_description.
|
34
|
+
def initialize(address, instruction_store, opcode, operands, size, address_increment, string, properties)
|
35
|
+
@address = address
|
36
|
+
@instruction_store = instruction_store
|
37
|
+
@opcode = opcode
|
38
|
+
@operands = operands
|
39
|
+
@size = size
|
40
|
+
@address_increment = address_increment
|
41
|
+
@string = string
|
42
|
+
|
43
|
+
modules = {
|
44
|
+
conditional_skip: ConditionalSkip,
|
45
|
+
goto: Goto,
|
46
|
+
return: Return,
|
47
|
+
call: Call
|
48
|
+
}
|
49
|
+
|
50
|
+
properties.each do |p|
|
51
|
+
mod = modules[p]
|
52
|
+
if !mod
|
53
|
+
raise ArgumentError, "Invalid property: #{p.inspect}."
|
54
|
+
end
|
55
|
+
extend mod
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Compares this instruction to another using the addresses. This means you can
|
60
|
+
# call +.sort+ on an array of instructions to put them in order by address.
|
61
|
+
def <=>(other)
|
62
|
+
self.address <=> other.address
|
63
|
+
end
|
64
|
+
|
65
|
+
# Human-readable string representation of the instruction.
|
66
|
+
def to_s
|
67
|
+
"Instruction(#{@instruction_store.address_description(address)}, #{@string})"
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
"#<#{self.class}:#{@instruction_store.address_description(address)}, #{@string}>"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns info about all the instructions that this instruction could directly lead to
|
75
|
+
# (not counting interrupts, returns, and not accounting
|
76
|
+
# at all for what happens after the last word in flash is executed).
|
77
|
+
# For instructions that pop from the call stack like RETURN and RETFIE, this will be
|
78
|
+
# the empty array.
|
79
|
+
# @return [Array(Transition)]
|
80
|
+
def transitions
|
81
|
+
@transitions ||= generate_transitions
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the transition from this instruction to the specified instruction
|
85
|
+
# or nil if no such transition exists.
|
86
|
+
# @return Transition
|
87
|
+
def transition_to(instruction)
|
88
|
+
@transitions.find { |t| t.next_instruction == instruction }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the addresses of all the instructions this instruction could directly lead to.
|
92
|
+
# @return [Array(Integer)]
|
93
|
+
def next_addresses
|
94
|
+
transitions.collect do |t|
|
95
|
+
t.next_instruction.address
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
# For certain opcodes, this method gets over-written.
|
101
|
+
def generate_transitions
|
102
|
+
[ advance(1) ]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Makes a transition representing the default behavior: the microcontroller
|
106
|
+
# will increment the program counter and execute the next instruction in memory.
|
107
|
+
def advance(num)
|
108
|
+
transition(address + num * size)
|
109
|
+
end
|
110
|
+
|
111
|
+
def transition(addr, attrs={})
|
112
|
+
next_instruction = @instruction_store.instruction(addr)
|
113
|
+
Transition.new(self, next_instruction, attrs)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
# Returns the address indicated by the operand 'k'.
|
118
|
+
# k is assumed to be a word address and it is assumed to be absolute
|
119
|
+
# k=0 is word 0 of memory, k=1 is word one.
|
120
|
+
# We need to multiply by the address increment because on PIC18
|
121
|
+
# flash addresses are actually byte-based instead of word-based.
|
122
|
+
def k_address
|
123
|
+
operands['k'] * @address_increment
|
124
|
+
end
|
125
|
+
|
126
|
+
### Modules that modify the behavior of the instruction. ###
|
127
|
+
|
128
|
+
|
129
|
+
# This module is mixed into any {Instruction} that represents a goto or branch.
|
130
|
+
module Goto
|
131
|
+
def generate_transitions
|
132
|
+
# Assumption: The GOTO instruction's k operand is absolute on all architectures
|
133
|
+
[ transition(k_address, non_local: true) ]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# This module is mixed into any {Instruction} that represents a conditional skip
|
138
|
+
# A conditional skip is an instruction that might cause the next instruction to be
|
139
|
+
# skipped depending on some condition.
|
140
|
+
module ConditionalSkip
|
141
|
+
def generate_transitions
|
142
|
+
[ advance(1), advance(2) ]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# This module is mixed into any {Instruction} that represents a return from a subroutine.
|
147
|
+
module Return
|
148
|
+
def generate_transitions
|
149
|
+
[]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# This module is mixed into any {Instruction} that represents a subroutine call.
|
154
|
+
module Call
|
155
|
+
def generate_transitions
|
156
|
+
[ transition(k_address, call_depth_change: 1), advance(1) ]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class Transition
|
161
|
+
attr_reader :next_instruction, :previous_instruction
|
162
|
+
|
163
|
+
def initialize(previous_instruction, next_instruction, attrs)
|
164
|
+
@previous_instruction = previous_instruction
|
165
|
+
@next_instruction = next_instruction
|
166
|
+
@attrs = attrs
|
167
|
+
end
|
168
|
+
|
169
|
+
def non_local?
|
170
|
+
@attrs.fetch(:non_local, false)
|
171
|
+
end
|
172
|
+
|
173
|
+
def call_depth_change
|
174
|
+
@attrs.fetch(:call_depth_change, 0)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RPicSim
|
2
|
+
# A very simple class that represents a label in firmware.
|
3
|
+
class Label
|
4
|
+
# The name of the label from the firmware.
|
5
|
+
# @return (Symbol)
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# The address/value of the label from the firmware.
|
9
|
+
# @return (Integer)
|
10
|
+
attr_reader :address
|
11
|
+
|
12
|
+
# Makes a new label with the specified name and address.
|
13
|
+
def initialize(name, address)
|
14
|
+
@name = name
|
15
|
+
@address = address
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the address of the label.
|
19
|
+
def to_i
|
20
|
+
address
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a nice string representation of the label.
|
24
|
+
def to_s
|
25
|
+
"<Label %s address=0x%x>" % [name, address]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Memory
|
2
|
+
# This object allows read and write access to the current data
|
3
|
+
# stored in a memory space of the simulated device.
|
4
|
+
#
|
5
|
+
# The behavior of this class differs depending on what kind of Memory
|
6
|
+
# it represents, as shown in the table below:
|
7
|
+
#
|
8
|
+
# Address type Read/write chunk
|
9
|
+
# Any type of RAM: Byte address 1 byte (8 bits)
|
10
|
+
# Midrange Flash: Word address 1 word (14 bits)
|
11
|
+
# PIC18 flash: Byte address 1 word (16 bits)
|
12
|
+
#
|
13
|
+
# @param mplab_memory [Mplab::Memory]
|
14
|
+
def initialize(mplab_memory)
|
15
|
+
@mplab_memory = mplab_memory
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_word(address, value)
|
19
|
+
@mplab_memory.write_word(address, value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_word(address)
|
23
|
+
@mplab_memory.read_word(address)
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_valid_address?(address)
|
27
|
+
@mplab_memory.is_valid_address?(address)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module RPicSim
|
4
|
+
# This class can attach to an MPLAB X memory class and watch for changes in it, and
|
5
|
+
# report those changes as a hash associating variable names to new values. This is
|
6
|
+
# very useful for writing tests that assert that not only were the desired variables
|
7
|
+
# written to, but also that no other variables were written to.
|
8
|
+
#
|
9
|
+
# This class does not analyze the current instruction being executed; it uses the
|
10
|
+
# Attach method of the MPLAB X memory classes. This means that it doesn't work properly
|
11
|
+
# in some versions of MPLAB X. See {file:docs/Flaws.textile}.
|
12
|
+
class MemoryWatcher
|
13
|
+
attr_accessor :var_names_ignored
|
14
|
+
attr_accessor :var_names_ignored_on_first_step
|
15
|
+
|
16
|
+
# Creates a new instance.
|
17
|
+
# @param sim [Sim]
|
18
|
+
# @param memory [Mplab::MplabMemory] The memory to watch
|
19
|
+
# @param vars [Array(Variable)]
|
20
|
+
def initialize(sim, memory, vars)
|
21
|
+
# Populate the @vars_by_address instance hash
|
22
|
+
@vars_by_address = {}
|
23
|
+
vars.each do |var|
|
24
|
+
var.addresses.each do |address|
|
25
|
+
if @vars_by_address[address]
|
26
|
+
raise "Variable %s overlaps with %s at 0x%x" %
|
27
|
+
[var, @vars_by_address[address], address]
|
28
|
+
end
|
29
|
+
@vars_by_address[address] = var
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@sim = sim
|
34
|
+
@memory = memory
|
35
|
+
@memory.on_change { |ar| handle_change(ar) }
|
36
|
+
|
37
|
+
@vars_written = Set.new
|
38
|
+
@var_names_ignored = default_var_names_ignored(sim.device)
|
39
|
+
@var_names_ignored_on_first_step = default_var_names_ignored_on_first_step(sim.device)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generate a nice report of what variables have been written to since the
|
43
|
+
# last time {#clear} was called.
|
44
|
+
# @return [Hash] A hash where the keys are names (as symbols) of variables that were
|
45
|
+
# written to, and the value is the variable's current value.
|
46
|
+
# If an address was written to that does not correspond to a variable, the key for
|
47
|
+
# that will just be address as an integer.
|
48
|
+
def writes
|
49
|
+
hash = {}
|
50
|
+
@vars_written.each do |var|
|
51
|
+
if var.is_a? Integer
|
52
|
+
hash[var] = @memory.read_word(var)
|
53
|
+
else
|
54
|
+
hash[var.name] = var.value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
hash
|
58
|
+
end
|
59
|
+
|
60
|
+
# Clears the record of what variables have been written to.
|
61
|
+
def clear
|
62
|
+
@vars_written.clear
|
63
|
+
end
|
64
|
+
|
65
|
+
def default_var_names_ignored(device_name)
|
66
|
+
# The datasheet says the PCLATH is not affected by pushing or popping the stack, but
|
67
|
+
# we still get spurious events for it when a return instruction is executed.
|
68
|
+
|
69
|
+
[:PCL, :PCLATH, :STATUS]
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_var_names_ignored_on_first_step(device_name)
|
73
|
+
#TODO: get rid of this stuff? people should just have a goto or something harmless at
|
74
|
+
# the beginning of their program and take a single step like we do.
|
75
|
+
[:PORTA, :LATA, :OSCCON, :PMCON2, :INTCON]
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_change(address_ranges)
|
79
|
+
addresses = address_ranges.flat_map(&:to_a)
|
80
|
+
vars = addresses.map { |a| @vars_by_address[a] || a }
|
81
|
+
|
82
|
+
remove_vars(vars, @var_names_ignored)
|
83
|
+
remove_vars(vars, @var_names_ignored_on_first_step) if @sim.cycle_count <= 1
|
84
|
+
|
85
|
+
# The line below works because @vars_written is a Set, not a Hash.
|
86
|
+
@vars_written.merge vars
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def remove_vars(vars, var_names_to_remove)
|
91
|
+
vars.reject! do |key, val|
|
92
|
+
name = key.is_a?(Integer) ? key : key.name
|
93
|
+
var_names_to_remove.include?(name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module RPicSim
|
5
|
+
module Mplab
|
6
|
+
# Returns a Pathname object representing the directory of the MPLAB X we are using.
|
7
|
+
# This can either come from the +RPICSIM_MPLABX+ environment variable or it can
|
8
|
+
# be auto-detected by looking in the standard places that MPLAB X is installed.
|
9
|
+
# @return [Pathname]
|
10
|
+
def self.dir
|
11
|
+
@dir ||= begin
|
12
|
+
dir = ENV['RPICSIM_MPLABX']
|
13
|
+
|
14
|
+
dir ||= [
|
15
|
+
"C:/Program Files (x86)/Microchip/MPLABX/",
|
16
|
+
"C:/Program Files/Microchip/MPLABX/",
|
17
|
+
# TODO: add entries here for MPLAB X folders in Linux and Mac OS X
|
18
|
+
].detect { |d| File.directory?(d) }
|
19
|
+
|
20
|
+
if !dir
|
21
|
+
raise "Cannot find MPLABX. Install it in the standard location or " +
|
22
|
+
"set the RPICSIM_MPASM environment variable to its full path."
|
23
|
+
end
|
24
|
+
|
25
|
+
if !File.directory?(dir)
|
26
|
+
raise "MPLABX directory does not exist: #{dir}"
|
27
|
+
end
|
28
|
+
|
29
|
+
Pathname(dir)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds all the needed MPLAB X jar files to the classpath so we can use the
|
34
|
+
# classes.
|
35
|
+
def self.load_dependencies
|
36
|
+
%w{ mplab_ide/mdbcore/modules/*.jar
|
37
|
+
mplab_ide/mplablibs/modules/*.jar
|
38
|
+
mplab_ide/mplablibs/modules/ext/*.jar
|
39
|
+
mplab_ide/platform/lib/org-openide-util*.jar
|
40
|
+
mplab_ide/platform/lib/org-openide-util.jar
|
41
|
+
mplab_ide/mdbcore/modules/ext/org-openide-filesystems.jar
|
42
|
+
}.each do |pattern|
|
43
|
+
Dir.glob(dir + pattern).each do |jar_file|
|
44
|
+
$CLASSPATH << jar_file
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Do a quick test to make sure we can load some MPLAB X classes.
|
49
|
+
# In case MPLAB X was uninstalled and its directory remains, this can provide
|
50
|
+
# a useful error message to the user.
|
51
|
+
begin
|
52
|
+
org.openide.util.Lookup
|
53
|
+
com.microchip.mplab.mdbcore.simulator.Simulator
|
54
|
+
rescue NameError
|
55
|
+
$stderr.puts "Failed to load MPLAB X classes.\n" +
|
56
|
+
"MPLAB X dir: #{dir}\nClass path:\n" + $CLASSPATH.to_a.join("\n") + "\n\n"
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
load_dependencies
|
62
|
+
|
63
|
+
|
64
|
+
# JRuby makes it hard to access packages with capital letters in their names.
|
65
|
+
# This is a workaround to let us access those packages.
|
66
|
+
module CapitalizedPackages
|
67
|
+
include_package "com.microchip.mplab.libs.MPLABDocumentLocator"
|
68
|
+
end
|
69
|
+
|
70
|
+
# The com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator class from MPLAB X.
|
71
|
+
DocumentLocator = CapitalizedPackages::MPLABDocumentLocator
|
72
|
+
|
73
|
+
# Returns a string like "1.95" representing the version of MPLAB X we are using.
|
74
|
+
# NOTE: You should probably NOT be doing this to work around flaws in MPLAB X.
|
75
|
+
# Instead, you should add a new entry in flaws.rb and then use
|
76
|
+
# RPicSim::Flaws[:FLAWNAME] to see if the flaw exists and choose the appropriate workaround.
|
77
|
+
def self.version
|
78
|
+
# This implementation is not pretty; I would prefer to just find the right function
|
79
|
+
# to call.
|
80
|
+
@mplabx_version ||= begin
|
81
|
+
paths = Dir.glob(dir + "Uninstall_MPLAB_X_IDE_v*.dat")
|
82
|
+
if paths.empty?
|
83
|
+
raise "Cannot detect MPLAB X version. The Uninstall_MPLAB_X_IDE_v*.dat file was not found in #{mplabx_dir}."
|
84
|
+
end
|
85
|
+
match_data = paths.first.match(/v([0-9][0-9\.]*[0-9]+)\./)
|
86
|
+
match_data[1]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Mutes the standard output, calls the given block, and then unmutes it.
|
91
|
+
def self.mute_stdout
|
92
|
+
begin
|
93
|
+
orig = java.lang.System.out
|
94
|
+
java.lang.System.setOut(java.io.PrintStream.new(NullOutputStream.new))
|
95
|
+
yield
|
96
|
+
ensure
|
97
|
+
java.lang.System.setOut(orig)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Mutes a particular type of exception printed by NetBeans,
|
102
|
+
# calls the given block, then unmutes it.
|
103
|
+
def self.mute_exceptions
|
104
|
+
log = java.util.logging.Logger.getLogger('org.openide.util.Exceptions')
|
105
|
+
level = log.getLevel
|
106
|
+
begin
|
107
|
+
log.setLevel(java.util.logging.Level::OFF)
|
108
|
+
yield
|
109
|
+
ensure
|
110
|
+
log.setLevel(level)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# The MCDisAsm class is the disassembly provided by MPLAB X.
|
115
|
+
# We added this part so we could get access to its strategy object, but
|
116
|
+
# we are not currently using that feature.
|
117
|
+
class Java::ComMicrochipMplabMdbcoreDisasm::MCDisAsm
|
118
|
+
field_accessor :strategy
|
119
|
+
end
|
120
|
+
|
121
|
+
# This class helps us suppress the standard output temporarily.
|
122
|
+
class NullOutputStream < Java::JavaIo::OutputStream
|
123
|
+
def write(b)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Lookup = org.openide.util.Lookup
|
128
|
+
Mdbcore = com.microchip.mplab.mdbcore
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
# We want as much awareness as possible; if it becomes a problem we can change this.
|
134
|
+
com.microchip.mplab.logger.MPLABLogger.mplog.setLevel(java.util.logging.Level::ALL)
|
135
|
+
|
136
|
+
require_relative 'mplab/mplab_pin'
|
137
|
+
require_relative 'mplab/mplab_assembly'
|
138
|
+
require_relative 'mplab/mplab_program_file'
|