avruby 0.5.1

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +28 -0
  3. data/README.md +17 -0
  4. data/bin/avruby_shell +162 -0
  5. data/lib/avr.rb +41 -0
  6. data/lib/avr/argument.rb +23 -0
  7. data/lib/avr/clock.rb +99 -0
  8. data/lib/avr/cpu.rb +254 -0
  9. data/lib/avr/device.rb +249 -0
  10. data/lib/avr/device/atmel_atmega328p.rb +181 -0
  11. data/lib/avr/instruction.rb +72 -0
  12. data/lib/avr/memory.rb +163 -0
  13. data/lib/avr/memory/eeprom.rb +65 -0
  14. data/lib/avr/memory/flash.rb +13 -0
  15. data/lib/avr/memory/memory_byte.rb +54 -0
  16. data/lib/avr/memory/sram.rb +13 -0
  17. data/lib/avr/opcode.rb +237 -0
  18. data/lib/avr/opcode/branch/conditional.rb +61 -0
  19. data/lib/avr/opcode/branch/return.rb +23 -0
  20. data/lib/avr/opcode/branch/unconditional.rb +74 -0
  21. data/lib/avr/opcode/break.rb +14 -0
  22. data/lib/avr/opcode/compare.rb +66 -0
  23. data/lib/avr/opcode/data/immediate.rb +14 -0
  24. data/lib/avr/opcode/data/program.rb +25 -0
  25. data/lib/avr/opcode/data/sram.rb +222 -0
  26. data/lib/avr/opcode/data/stack.rb +22 -0
  27. data/lib/avr/opcode/io/bit.rb +22 -0
  28. data/lib/avr/opcode/io/in_out.rb +38 -0
  29. data/lib/avr/opcode/math/addition.rb +120 -0
  30. data/lib/avr/opcode/math/bitwise.rb +170 -0
  31. data/lib/avr/opcode/math/multiplication.rb +23 -0
  32. data/lib/avr/opcode/math/subtraction.rb +137 -0
  33. data/lib/avr/opcode/nop.rb +14 -0
  34. data/lib/avr/opcode/opcodes.rb +24 -0
  35. data/lib/avr/opcode/operand_parsers.rb +75 -0
  36. data/lib/avr/opcode/register.rb +37 -0
  37. data/lib/avr/opcode/sleep.rb +14 -0
  38. data/lib/avr/opcode/sreg.rb +56 -0
  39. data/lib/avr/opcode/wdr.rb +14 -0
  40. data/lib/avr/opcode_decoder.rb +202 -0
  41. data/lib/avr/oscillator.rb +33 -0
  42. data/lib/avr/port.rb +107 -0
  43. data/lib/avr/register.rb +18 -0
  44. data/lib/avr/register/memory_byte_register.rb +27 -0
  45. data/lib/avr/register/memory_byte_register_with_named_bits.rb +85 -0
  46. data/lib/avr/register/register_file.rb +59 -0
  47. data/lib/avr/register/register_pair.rb +46 -0
  48. data/lib/avr/register/sp.rb +41 -0
  49. data/lib/avr/register/sreg.rb +12 -0
  50. data/lib/avr/register_with_bit_number.rb +40 -0
  51. data/lib/avr/register_with_displacement.rb +31 -0
  52. data/lib/avr/register_with_modification.rb +35 -0
  53. data/lib/avr/register_with_named_bit.rb +36 -0
  54. data/lib/avr/value.rb +45 -0
  55. data/lib/avr/version.rb +6 -0
  56. metadata +182 -0
@@ -0,0 +1,163 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'avr/memory/memory_byte'
5
+ require 'intel_hex'
6
+
7
+ module AVR
8
+ class Memory
9
+ extend T::Sig
10
+ extend T::Helpers
11
+ abstract!
12
+
13
+ class Watch
14
+ extend T::Sig
15
+
16
+ sig do
17
+ params(
18
+ proc: T.nilable(
19
+ T.proc.params(
20
+ memory_byte: MemoryByte,
21
+ old_value: Integer,
22
+ new_value: Integer,
23
+ ).void
24
+ ),
25
+ block: T.nilable(
26
+ T.proc.params(
27
+ memory_byte: MemoryByte,
28
+ old_value: Integer,
29
+ new_value: Integer,
30
+ ).void
31
+ )
32
+ ).void
33
+ end
34
+ def initialize(proc = nil, &block)
35
+ @watch_proc = T.let(proc || T.must(block).to_proc, Proc)
36
+ end
37
+
38
+ sig { params(memory_byte: MemoryByte, old_value: Integer, new_value: Integer).void }
39
+ def notify(memory_byte, old_value, new_value)
40
+ @watch_proc.call(memory_byte, old_value, new_value)
41
+ end
42
+ end
43
+
44
+ class WatchBinding
45
+ extend T::Sig
46
+
47
+ sig { returns(Watch) }
48
+ attr_reader :watch
49
+
50
+ sig { returns(T.nilable(T::Array[Integer])) }
51
+ attr_reader :filter
52
+
53
+ sig { params(watch: Watch, filter: T.nilable(T::Array[Integer])).void }
54
+ def initialize(watch, filter = nil)
55
+ @watch = watch
56
+ @filter = filter
57
+ end
58
+
59
+ sig { params(address: Integer).returns(T::Boolean) }
60
+ def include?(address)
61
+ if filter
62
+ return true if filter&.include?(address)
63
+ false
64
+ end
65
+ true
66
+ end
67
+ end
68
+
69
+ sig { returns(String) }
70
+ attr_reader :name
71
+
72
+ sig { returns(Integer) }
73
+ attr_reader :size
74
+
75
+ sig { returns(T::Array[MemoryByte]) }
76
+ attr_reader :memory
77
+
78
+ sig { returns(T::Array[WatchBinding]) }
79
+ attr_reader :watches
80
+
81
+ sig { params(name: String, size: Integer, value: Integer).void }
82
+ def initialize(name, size, value = 0)
83
+ @name = name
84
+ @size = size
85
+ @memory = T.let(
86
+ size.times.map { |address| MemoryByte.new(self, address, value) },
87
+ T::Array[MemoryByte]
88
+ )
89
+ @watches = T.let([], T::Array[WatchBinding])
90
+ end
91
+
92
+ sig { returns(String) }
93
+ def inspect
94
+ "#<#{self.class.name} size=#{size}>"
95
+ end
96
+
97
+ sig { void }
98
+ def reset
99
+ memory.each do |byte|
100
+ byte.value = 0
101
+ end
102
+ end
103
+
104
+ sig { params(memory_byte: MemoryByte, old_value: Integer, new_value: Integer).void }
105
+ def notify(memory_byte, old_value, new_value)
106
+ watches.each do |watch|
107
+ if watch.include?(memory_byte.address)
108
+ watch.watch.notify(memory_byte, old_value, new_value)
109
+ end
110
+ end
111
+ end
112
+
113
+ sig { params(watch: Watch, filter: T.nilable(T::Array[Integer])).void }
114
+ def unshift_watch(watch, filter = nil)
115
+ watches.unshift(WatchBinding.new(watch, filter))
116
+ end
117
+
118
+ sig { params(watch: Watch, filter: T.nilable(T::Array[Integer])).void }
119
+ def push_watch(watch, filter = nil)
120
+ watches.push(WatchBinding.new(watch, filter))
121
+ end
122
+
123
+ sig do
124
+ params(
125
+ filter: T.untyped,
126
+ block: T.proc.params(
127
+ memory_byte: MemoryByte,
128
+ old_value: Integer,
129
+ new_value: Integer,
130
+ ).void
131
+ ).returns(Watch)
132
+ end
133
+ def watch(filter = nil, &block)
134
+ watch = Watch.new(block.to_proc)
135
+ push_watch(watch, filter.is_a?(Integer) ? [filter] : filter)
136
+ watch
137
+ end
138
+
139
+ sig { params(address: Integer).returns(Integer) }
140
+ def word(address)
141
+ byte_address = address << 1
142
+ (T.must(memory[byte_address + 1]).value << 8) | T.must(memory[byte_address]).value
143
+ end
144
+
145
+ sig { params(address: Integer, value: Integer).void }
146
+ def set_word(address, value)
147
+ byte_address = address << 1
148
+ T.must(memory[byte_address + 1]).value = (value & 0xff00) >> 8
149
+ T.must(memory[byte_address]).value = value & 0x00ff
150
+ end
151
+
152
+ sig { params(filename: String).returns(Integer) }
153
+ def load_from_intel_hex(filename)
154
+ ihex = IntelHex::FileReader.new(filename)
155
+ sum = 0
156
+ ihex.each_byte_with_address do |byte, address|
157
+ T.must(memory[address]).value = byte
158
+ sum += 1
159
+ end
160
+ sum
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,65 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module AVR
5
+ class EEPROM < Memory
6
+ extend T::Sig
7
+
8
+ ERASED_VALUE = 0xff
9
+
10
+ sig { returns(CPU) }
11
+ attr_reader :cpu
12
+
13
+ sig { params(size: Integer, cpu: CPU).void }
14
+ def initialize(size, cpu)
15
+ super('EEPROM', size, ERASED_VALUE)
16
+ attach(cpu)
17
+ end
18
+
19
+ sig { params(cpu: CPU).void }
20
+ def attach(cpu)
21
+ @cpu = cpu
22
+ @watched_memory_bytes = {
23
+ cpu.EEARL.memory_byte => :EEARL,
24
+ cpu.EEARH.memory_byte => :EEARH,
25
+ cpu.EECR.memory_byte => :EECR,
26
+ cpu.EEDR.memory_byte => :EEDR,
27
+ }
28
+
29
+ @cpu.sram.watch(@watched_memory_bytes.keys.map(&:address)) do |memory_byte, old_value, new_value|
30
+ case @watched_memory_bytes[memory_byte]
31
+ when :EECR
32
+ handle_eecr(old_value, new_value)
33
+ end
34
+ end
35
+ end
36
+
37
+ sig { params(old_value: Integer, new_value: Integer).void }
38
+ def handle_eecr(old_value, new_value)
39
+ old_eecr = cpu.EECR.hash_for_value(old_value)
40
+ new_eecr = cpu.EECR.hash_for_value(new_value)
41
+
42
+ if !old_eecr[:EEMPE] && new_eecr[:EEMPE]
43
+ cpu.notify_at_tick(cpu.clock.ticks + 4) do
44
+ cpu.EECR.EEMPE = false
45
+ end
46
+ end
47
+
48
+ if !old_eecr[:EEPE] && new_eecr[:EEPE] && new_eecr[:EEMPE]
49
+ if (!new_eecr[:EEPM0] && !new_eecr[:EEPM1]) || new_eecr[:EEPM1]
50
+ T.must(memory[(cpu.EEARH.value << 8) | cpu.EEARL.value]).value = cpu.EEDR.value
51
+ elsif new_eecr[:EEPM0]
52
+ T.must(memory[(cpu.EEARH.value << 8) | cpu.EEARL.value]).value = ERASED_VALUE
53
+ end
54
+ cpu.EECR.from_h({ EEMPE: false, EEPE: false })
55
+ end
56
+
57
+ if !old_eecr[:EERE] && new_eecr[:EERE]
58
+ cpu.EEDR.value = T.must(memory[(cpu.EEARH.value << 8) | cpu.EEARL.value]).value
59
+ cpu.EECR.EERE = false
60
+ end
61
+
62
+ cpu.interrupt(:EE_READY) if cpu.sreg.I && new_eecr[:EERIE]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AVR
5
+ class Flash < Memory
6
+ extend T::Sig
7
+
8
+ sig { params(size: Integer).void }
9
+ def initialize(size)
10
+ super('Flash', size, 0xff)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AVR
5
+ class MemoryByte
6
+ extend T::Sig
7
+
8
+ sig { returns(Memory) }
9
+ attr_reader :memory
10
+
11
+ sig { returns(Integer) }
12
+ attr_reader :address
13
+
14
+ sig { returns(Integer) }
15
+ attr_reader :value
16
+
17
+ sig { params(memory: Memory, address: Integer, value: Integer).void }
18
+ def initialize(memory, address, value)
19
+ @memory = memory
20
+ @address = address
21
+ @value = value
22
+ end
23
+
24
+ sig { returns(String) }
25
+ def format
26
+ '%02x'
27
+ end
28
+
29
+ sig { returns(Integer) }
30
+ def to_i
31
+ value.to_i
32
+ end
33
+
34
+ sig { returns(String) }
35
+ def to_s
36
+ value.to_s
37
+ end
38
+
39
+ sig { returns(String) }
40
+ def chr
41
+ value.chr
42
+ end
43
+
44
+ sig { params(new_value: Integer).void }
45
+ def value=(new_value)
46
+ return if new_value == value
47
+ raise "Value #{new_value} out of range" unless (0..255).include?(new_value)
48
+
49
+ old_value = value
50
+ @value = new_value
51
+ memory.notify(self, old_value, new_value)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AVR
5
+ class SRAM < Memory
6
+ extend T::Sig
7
+
8
+ sig { params(size: Integer).void }
9
+ def initialize(size)
10
+ super('SRAM', size, 0)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,237 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AVR
5
+ extend T::Sig
6
+
7
+ class Opcode
8
+ extend T::Sig
9
+
10
+ class OpcodeException < RuntimeError; end
11
+ class IncorrectArgumentCount < OpcodeException; end
12
+ class RegisterExpected < OpcodeException; end
13
+ class UpperRegisterExpected < OpcodeException; end
14
+ class WordRegisterExpected < OpcodeException; end
15
+ class ByteConstantExpected < OpcodeException; end
16
+ class WordConstantExpected < OpcodeException; end
17
+ class IoAddressExpected < OpcodeException; end
18
+ class LowerIoAddressExpected < OpcodeException; end
19
+ class AbsolutePcExpected < OpcodeException; end
20
+ class NearRelativePcExpected < OpcodeException; end
21
+ class FarRelativePcExpected < OpcodeException; end
22
+ class BitNumberExpected < OpcodeException; end
23
+ class StatusRegisterBitExpected < OpcodeException; end
24
+ class ConstantOutOfRange < OpcodeException; end
25
+
26
+ # rubocop:disable Layout/HashAlignment
27
+ OPCODE_ARGUMENT_TYPES = T.let(
28
+ {
29
+ sreg_flag: '%s',
30
+ near_relative_pc: proc { |arg| '.%+d' % [2 * arg] },
31
+ far_relative_pc: proc { |arg| '.%+d' % [2 * arg] },
32
+ absolute_pc: proc { |arg| '0x%04x' % [2 * arg] },
33
+ byte: '0x%02x',
34
+ word: '0x%04x',
35
+ register: '%s',
36
+ register_pair: proc { |arg| '%s:%s' % [arg[0], arg[1]] },
37
+ word_register: '%s',
38
+ modifying_word_register: proc { |arg|
39
+ if arg.is_a?(RegisterPair)
40
+ '%s' % arg
41
+ else
42
+ '%s%s%s' % [
43
+ arg[1] == :pre_decrement ? '-' : '',
44
+ arg[0].to_s,
45
+ arg[1] == :post_increment ? '+' : '',
46
+ ]
47
+ end
48
+ },
49
+ displaced_word_register: proc { |arg|
50
+ '%s%+d' % [arg.register.name, arg.displacement]
51
+ },
52
+ register_with_bit_number: '%s',
53
+ io_address: '0x%02x',
54
+ lower_io_address: '0x%02x',
55
+ bit_number: '%d',
56
+ }.freeze,
57
+ T::Hash[Symbol, T.any(String, T.proc.params(arg: T::Array[Integer]).returns(String))]
58
+ )
59
+ # rubocop:enable Layout/HashAlignment
60
+
61
+ sig { returns(Symbol) }
62
+ attr_reader :mnemonic
63
+
64
+ sig { returns(T::Array[Symbol]) }
65
+ attr_reader :arg_types
66
+
67
+ sig { returns(T::Array[Symbol]) }
68
+ attr_reader :sreg_flags
69
+
70
+ ProcType = T.type_alias do
71
+ T.proc.params(
72
+ cpu: CPU,
73
+ memory: T.nilable(Memory),
74
+ args: Argument::ArrayType
75
+ ).void
76
+ end
77
+
78
+ sig { returns(Opcode::ProcType) }
79
+ attr_reader :opcode_proc
80
+
81
+ ExtractedOperandHashType = T.type_alias { T::Hash[Symbol, Integer] }
82
+ OperandValueHashType = T.type_alias { T::Hash[Symbol, Argument::ValueType] }
83
+
84
+ sig do
85
+ params(
86
+ mnemonic: Symbol,
87
+ arg_types: T::Array[Symbol],
88
+ sreg_flags: T::Array[Symbol],
89
+ opcode_proc: Opcode::ProcType
90
+ ).void
91
+ end
92
+ def initialize(mnemonic, arg_types, sreg_flags, opcode_proc)
93
+ @mnemonic = mnemonic
94
+ @arg_types = arg_types
95
+ @sreg_flags = sreg_flags
96
+ @opcode_proc = opcode_proc
97
+ arg_types.each do |arg_type|
98
+ raise "Unknown Opcode argument type: #{arg_type}" unless OPCODE_ARGUMENT_TYPES[arg_type]
99
+ end
100
+ end
101
+
102
+ sig { params(arg: T.untyped, arg_number: Integer).returns(T.nilable(T.class_of(OpcodeException))) }
103
+ def validate_arg(arg, arg_number)
104
+ case arg_types[arg_number]
105
+ when :register
106
+ return RegisterExpected unless arg.is_a?(Register)
107
+ when :word_register
108
+ return WordRegisterExpected unless arg.is_a?(RegisterPair)
109
+ when :byte
110
+ return ByteConstantExpected unless arg.is_a?(Value)
111
+ return ConstantOutOfRange unless arg.value >= 0x00 && arg.value <= 0xff
112
+ when :word
113
+ return WordConstantExpected unless arg.is_a?(Value)
114
+ return ConstantOutOfRange unless arg.value >= 0x0000 && arg.value <= 0xffff
115
+ when :absolute_pc
116
+ return AbsolutePcExpected unless arg.is_a?(Value)
117
+ return ConstantOutOfRange unless arg.value >= 0 && arg.value <= (2**22).to_i - 1
118
+ when :near_relative_pc
119
+ return NearRelativePcExpected unless arg.is_a?(Value)
120
+ return ConstantOutOfRange unless arg.value >= -64 && arg.value <= 63
121
+ when :far_relative_pc
122
+ return FarRelativePcExpected unless arg.is_a?(Value)
123
+ return ConstantOutOfRange unless arg.value >= -2048 && arg.value <= 2047
124
+ when :io_address
125
+ return IoAddressExpected unless arg.is_a?(Value)
126
+ return ConstantOutOfRange unless arg.value >= 0 && arg.value <= 63
127
+ when :lower_io_address
128
+ return IoAddressExpected unless arg.is_a?(Value)
129
+ return ConstantOutOfRange unless arg.value >= 0 && arg.value <= 31
130
+ when :register_with_bit_number
131
+ return RegisterExpected unless arg.register.is_a?(Register)
132
+ return BitNumberExpected unless arg.bit_number.is_a?(Integer)
133
+ return ConstantOutOfRange unless arg.bit_number >= 0 && arg.bit_number <= 7
134
+ when :sreg_flag
135
+ return StatusRegisterBitExpected unless arg.is_a?(Value)
136
+ return StatusRegisterBitExpected unless arg.value == 0 || arg.value == 1
137
+ end
138
+ end
139
+
140
+ sig { params(args: T::Array[T.untyped]).returns(T::Boolean) }
141
+ def validate(args)
142
+ raise IncorrectArgumentCount unless args.size == arg_types.size
143
+
144
+ args.each_with_index do |arg, i|
145
+ arg_exception = validate_arg(arg, i)
146
+
147
+ raise arg_exception, "Argument #{i} (#{arg}) invalid for #{arg_types[i]}" if arg_exception
148
+ end
149
+
150
+ true
151
+ end
152
+
153
+ sig { params(args: T::Array[T.untyped]).returns(T::Array[String]) }
154
+ def format_args(args)
155
+ formatted_args = []
156
+ args.each_with_index do |arg, i|
157
+ arg_formatter = OPCODE_ARGUMENT_TYPES[T.must(arg_types[i])]
158
+ case arg_formatter
159
+ when String
160
+ formatted_args << (arg_formatter % arg)
161
+ when Proc
162
+ formatted_args << arg_formatter.call(arg)
163
+ else
164
+ raise "Unknown argument formatter (#{arg_formatter.class}) for #{arg}"
165
+ end
166
+ end
167
+ formatted_args
168
+ end
169
+
170
+ sig { returns(String) }
171
+ def inspect
172
+ "#<#{self.class.name} #{mnemonic} #{arg_types}>"
173
+ end
174
+
175
+ sig { params(cpu: CPU, memory: T.nilable(Memory), args: T::Array[T.untyped]).void }
176
+ def execute(cpu, memory, args)
177
+ opcode_proc.call(cpu, memory, args)
178
+ end
179
+
180
+ @opcodes = T.let({}, T::Hash[Symbol, Opcode])
181
+
182
+ class << self
183
+ extend T::Sig
184
+ sig { returns(T::Hash[Symbol, Opcode]) }
185
+ attr_reader :opcodes
186
+ end
187
+
188
+ sig { params(cpu: CPU, byte: Integer).returns(Integer) }
189
+ def self.stack_push(cpu, byte)
190
+ cpu.sram.memory.fetch(cpu.sp.value).value = byte
191
+ cpu.sp.decrement
192
+ end
193
+
194
+ sig { params(cpu: CPU, word: Integer).returns(Integer) }
195
+ def self.stack_push_word(cpu, word)
196
+ stack_push(cpu, (word & 0xff00) >> 8)
197
+ stack_push(cpu, (word & 0x00ff))
198
+ end
199
+
200
+ sig { params(cpu: CPU).returns(Integer) }
201
+ def self.stack_pop(cpu)
202
+ cpu.sp.increment
203
+ cpu.sram.memory.fetch(cpu.sp.value).value
204
+ end
205
+
206
+ sig { params(cpu: CPU).returns(Integer) }
207
+ def self.stack_pop_word(cpu)
208
+ stack_pop(cpu) | (stack_pop(cpu) << 8)
209
+ end
210
+
211
+ sig do
212
+ params(
213
+ mnemonic: Symbol,
214
+ arg_types: T::Array[Symbol],
215
+ sreg_flags: T::Array[Symbol],
216
+ block: T.nilable(Opcode::ProcType)
217
+ ).returns(Opcode)
218
+ end
219
+ def self.opcode(mnemonic, arg_types = [], sreg_flags = [], &block)
220
+ raise 'No block given' unless block_given?
221
+
222
+ opcodes[mnemonic] = Opcode.new(mnemonic, arg_types, sreg_flags, block.to_proc)
223
+ end
224
+
225
+ sig { params(pattern: String, mnemonic: Symbol, block: OpcodeDecoder::OpcodeDefinition::ProcType).void }
226
+ def self.decode(pattern, mnemonic, &block)
227
+ OpcodeDecoder.add_opcode_definition(
228
+ OpcodeDecoder::OpcodeDefinition.new(pattern, mnemonic, block.to_proc)
229
+ )
230
+ end
231
+
232
+ sig { params(pattern: String, block: T.untyped).void }
233
+ def self.parse_operands(pattern, &block)
234
+ OpcodeDecoder.add_operand_parser(OpcodeDecoder::OperandParser.new(pattern, block.to_proc))
235
+ end
236
+ end
237
+ end