avruby 0.5.1

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