rpicsim 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|