Class: AVR::Opcode

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/avr/opcode.rb,
lib/avr/opcode/nop.rb,
lib/avr/opcode/wdr.rb,
lib/avr/opcode/sreg.rb,
lib/avr/opcode/break.rb,
lib/avr/opcode/sleep.rb,
lib/avr/opcode/io/bit.rb,
lib/avr/opcode/compare.rb,
lib/avr/opcode/register.rb,
lib/avr/opcode/data/sram.rb,
lib/avr/opcode/io/in_out.rb,
lib/avr/opcode/data/stack.rb,
lib/avr/opcode/data/program.rb,
lib/avr/opcode/math/bitwise.rb,
lib/avr/opcode/branch/return.rb,
lib/avr/opcode/math/addition.rb,
lib/avr/opcode/data/immediate.rb,
lib/avr/opcode/operand_parsers.rb,
lib/avr/opcode/math/subtraction.rb,
lib/avr/opcode/branch/conditional.rb,
lib/avr/opcode/math/multiplication.rb,
lib/avr/opcode/branch/unconditional.rb

Defined Under Namespace

Classes: AbsolutePcExpected, BitNumberExpected, ByteConstantExpected, ConstantOutOfRange, FarRelativePcExpected, IncorrectArgumentCount, IoAddressExpected, LowerIoAddressExpected, NearRelativePcExpected, OpcodeException, RegisterExpected, StatusRegisterBitExpected, UpperRegisterExpected, WordConstantExpected, WordRegisterExpected

Constant Summary collapse

OPCODE_ARGUMENT_TYPES =

rubocop:disable Layout/HashAlignment

T.let(
  {
    sreg_flag:          '%s',
    near_relative_pc:   proc { |arg| '.%+d' % [2 * arg] },
    far_relative_pc:    proc { |arg| '.%+d' % [2 * arg] },
    absolute_pc:        proc { |arg| '0x%04x' % [2 * arg] },
    byte:               '0x%02x',
    word:               '0x%04x',
    register:           '%s',
    register_pair:      proc { |arg| '%s:%s' % [arg[0], arg[1]] },
    word_register:      '%s',
    modifying_word_register: proc { |arg|
      if arg.is_a?(RegisterPair)
        '%s' % arg
      else
        '%s%s%s' % [
          arg[1] == :pre_decrement ? '-' : '',
          arg[0].to_s,
          arg[1] == :post_increment ? '+' : '',
        ]
      end
    },
    displaced_word_register: proc { |arg|
      '%s%+d' % [arg.register.name, arg.displacement]
    },
    register_with_bit_number: '%s',
    io_address:         '0x%02x',
    lower_io_address:   '0x%02x',
    bit_number:         '%d',
  }.freeze,
  T::Hash[Symbol, T.any(String, T.proc.params(arg: T::Array[Integer]).returns(String))]
)
ProcType =
T.type_alias do
  T.proc.params(
    cpu: CPU,
    memory: T.nilable(Memory),
    args: Argument::ArrayType
  ).void
end
ExtractedOperandHashType =
T.type_alias { T::Hash[Symbol, Integer] }
OperandValueHashType =
T.type_alias { T::Hash[Symbol, Argument::ValueType] }

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mnemonic, arg_types, sreg_flags, opcode_proc) ⇒ Opcode

Returns a new instance of Opcode.



92
93
94
95
96
97
98
99
100
# File 'lib/avr/opcode.rb', line 92

def initialize(mnemonic, arg_types, sreg_flags, opcode_proc)
  @mnemonic = mnemonic
  @arg_types = arg_types
  @sreg_flags = sreg_flags
  @opcode_proc = opcode_proc
  arg_types.each do |arg_type|
    raise "Unknown Opcode argument type: #{arg_type}" unless OPCODE_ARGUMENT_TYPES[arg_type]
  end
end

Class Attribute Details

.opcodesObject (readonly)

Returns the value of attribute opcodes.



185
186
187
# File 'lib/avr/opcode.rb', line 185

def opcodes
  @opcodes
end

Instance Attribute Details

#arg_typesObject (readonly)

Returns the value of attribute arg_types.



65
66
67
# File 'lib/avr/opcode.rb', line 65

def arg_types
  @arg_types
end

#mnemonicObject (readonly)

Returns the value of attribute mnemonic.



62
63
64
# File 'lib/avr/opcode.rb', line 62

def mnemonic
  @mnemonic
end

#opcode_procObject (readonly)

Returns the value of attribute opcode_proc.



79
80
81
# File 'lib/avr/opcode.rb', line 79

def opcode_proc
  @opcode_proc
end

#sreg_flagsObject (readonly)

Returns the value of attribute sreg_flags.



68
69
70
# File 'lib/avr/opcode.rb', line 68

def sreg_flags
  @sreg_flags
end

Class Method Details

.bit_jumble_for_lds_sts(k_in) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/avr/opcode/operand_parsers.rb', line 13

def self.bit_jumble_for_lds_sts(k_in)
  k_out  = k_in & 0b00001111
  k_out |= k_in & 0b01100000 >> 1
  k_out |= k_in & 0b00010000 << 2
  k_out |= ~(k_in & 0b00010000 << 3) & 0b10000000
  k_out
end

.decode(pattern, mnemonic, &block) ⇒ Object



226
227
228
229
230
# File 'lib/avr/opcode.rb', line 226

def self.decode(pattern, mnemonic, &block)
  OpcodeDecoder.add_opcode_definition(
    OpcodeDecoder::OpcodeDefinition.new(pattern, mnemonic, block.to_proc)
  )
end

.exchange_memory_byte_with_register(memory_byte, register, mnemonic) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/avr/opcode/data/sram.rb', line 175

def self.exchange_memory_byte_with_register(memory_byte, register, mnemonic)
  old_value = memory_byte.value
  case mnemonic
  when :xch
    memory_byte.value = register.value
  when :las
    memory_byte.value = register.value | old_value
  when :lac
    memory_byte.value = (~register.value & old_value) & 0xff
  when :lat
    memory_byte.value = register.value ^ old_value
  end
  register.value = old_value
end

.opcode(mnemonic, arg_types = [], sreg_flags = [], &block) ⇒ Object



219
220
221
222
223
# File 'lib/avr/opcode.rb', line 219

def self.opcode(mnemonic, arg_types = [], sreg_flags = [], &block)
  raise 'No block given' unless block_given?

  opcodes[mnemonic] = Opcode.new(mnemonic, arg_types, sreg_flags, block.to_proc)
end

.parse_operands(pattern, &block) ⇒ Object



233
234
235
# File 'lib/avr/opcode.rb', line 233

def self.parse_operands(pattern, &block)
  OpcodeDecoder.add_operand_parser(OpcodeDecoder::OperandParser.new(pattern, block.to_proc))
end

.set_sreg_for_add_adc(cpu, r, rd, rr) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/avr/opcode/math/addition.rb', line 35

def self.set_sreg_for_add_adc(cpu, r, rd, rr)
  b7  = (1 << 7)
  r7  = (r  & b7) != 0
  rd7 = (rd & b7) != 0
  rr7 = (rr & b7) != 0
  r3  = (r  & b7) != 0
  rd3 = (rd & b7) != 0
  rr3 = (rr & b7) != 0
  n   = r7
  v   = rd7 & rr7 & !r7 | !rd7 & !rr7 & r7
  c   = rd7 & rr7 | rr7 & !r7 | !r7 & rd7
  h   = rd3 & rr3 | rr3 & !r3 | !r3 & rd3

  cpu.sreg.from_h(
    {
      H: h,
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
      C: c,
    }
  )
end

.set_sreg_for_adiw(cpu, r, rd) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/avr/opcode/math/addition.rb', line 88

def self.set_sreg_for_adiw(cpu, r, rd)
  b15  = (1 << 15)
  b7   = (1 << 7)
  rdh7 = (rd & b7) != 0
  r15  = (r & b15) != 0
  v   = !rdh7 & r15
  n   = r15
  c   = !r15 & rdh7

  cpu.sreg.from_h(
    {
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
      C: c,
    }
  )
end

.set_sreg_for_and_or(cpu, value) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/avr/opcode/math/bitwise.rb', line 7

def self.set_sreg_for_and_or(cpu, value)
  r7 = (value & (1 << 7)) != 0

  cpu.sreg.from_h(
    {
      S: r7 ^ false,
      V: false,
      N: r7,
      Z: value.zero?,
    }
  )
end

.set_sreg_for_cp_cpi_cpc(cpu, r, rd, rr_k, mnemonic) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/avr/opcode/compare.rb', line 9

def self.set_sreg_for_cp_cpi_cpc(cpu, r, rd, rr_k, mnemonic)
  b7    = (1<<7)
  r7    = (r  & b7) != 0
  rd7   = (rd & b7) != 0
  rr_k7 = (rr_k & b7) != 0
  r3    = (r  & b7) != 0
  rd3   = (rd & b7) != 0
  rr_k3 = (rr_k & b7) != 0
  n     = r7
  v     = rd7 & rr_k7 & r7 | !rd7 & rr_k7 & r7
  c     = !rd7 & rr_k7 | rr_k7 & r7 | r7 & !rd7
  h     = !rd3 & rr_k3 | rr_k3 & r3 | r3 & !rd3

  z = r.zero?
  z = r.zero? ? cpu.sreg.Z : false if mnemonic == :cpc

  cpu.sreg.from_h(
    {
      H: h,
      S: n ^ v,
      V: v,
      N: n,
      Z: z,
      C: c,
    }
  )
end

.set_sreg_for_dec(cpu, r, rd) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/avr/opcode/math/subtraction.rb', line 8

def self.set_sreg_for_dec(cpu, r, rd)
  n = (r & (1 << 7)) != 0
  v = (rd == 0x80)

  cpu.sreg.from_h(
    {
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
    }
  )
end

.set_sreg_for_inc(cpu, r, rd) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/avr/opcode/math/addition.rb', line 8

def self.set_sreg_for_inc(cpu, r, rd)
  n = (r & (1 << 7)) != 0
  v = (rd == 0x7f)

  cpu.sreg.from_h(
    {
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
    }
  )
end

.set_sreg_for_sbiw(cpu, r, rd) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/avr/opcode/math/subtraction.rb', line 105

def self.set_sreg_for_sbiw(cpu, r, rd)
  b15  = (1 << 15)
  b7   = (1 << 7)
  rdh7 = (rd & b7) != 0
  r15  = (r & b15) != 0
  v   = r15 & !rdh7
  n   = r15
  c   = r15 & !rdh7

  cpu.sreg.from_h(
    {
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
      C: c,
    }
  )
end

.set_sreg_for_sub_sbc(cpu, r, rd, rr) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/avr/opcode/math/subtraction.rb', line 35

def self.set_sreg_for_sub_sbc(cpu, r, rd, rr)
  b7  = (1 << 7)
  r7  = (r  & b7) != 0
  rd7 = (rd & b7) != 0
  rr7 = (rr & b7) != 0
  r3  = (r  & b7) != 0
  rd3 = (rd & b7) != 0
  rr3 = (rr & b7) != 0

  n   = r7
  v   = rd7 & rr7 & !r7 | !rd7 & rr7 & r7
  c   = !rd7 & rr7 | rr7 & r7 | r7 & !rd7
  h   = !rd3 & rr3 | rr3 & r3 | r3 & !rd3

  cpu.sreg.from_h(
    {
      H: h,
      S: n ^ v,
      V: v,
      N: n,
      Z: r.zero?,
      C: c,
    }
  )
end

.stack_pop(cpu) ⇒ Object



201
202
203
204
# File 'lib/avr/opcode.rb', line 201

def self.stack_pop(cpu)
  cpu.sp.increment
  cpu.sram.memory.fetch(cpu.sp.value).value
end

.stack_pop_word(cpu) ⇒ Object



207
208
209
# File 'lib/avr/opcode.rb', line 207

def self.stack_pop_word(cpu)
  stack_pop(cpu) | (stack_pop(cpu) << 8)
end

.stack_push(cpu, byte) ⇒ Object



189
190
191
192
# File 'lib/avr/opcode.rb', line 189

def self.stack_push(cpu, byte)
  cpu.sram.memory.fetch(cpu.sp.value).value = byte
  cpu.sp.decrement
end

.stack_push_word(cpu, word) ⇒ Object



195
196
197
198
# File 'lib/avr/opcode.rb', line 195

def self.stack_push_word(cpu, word)
  stack_push(cpu, (word & 0xff00) >> 8)
  stack_push(cpu, (word & 0x00ff))
end

.twos_complement(value, bits) ⇒ Object



7
8
9
10
# File 'lib/avr/opcode/operand_parsers.rb', line 7

def self.twos_complement(value, bits)
  mask = (2**(bits - 1)).to_i
  -(value & mask) + (value & ~mask)
end

Instance Method Details

#execute(cpu, memory, args) ⇒ Object



176
177
178
# File 'lib/avr/opcode.rb', line 176

def execute(cpu, memory, args)
  opcode_proc.call(cpu, memory, args)
end

#format_args(args) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/avr/opcode.rb', line 154

def format_args(args)
  formatted_args = []
  args.each_with_index do |arg, i|
    arg_formatter = OPCODE_ARGUMENT_TYPES[T.must(arg_types[i])]
    case arg_formatter
    when String
      formatted_args << (arg_formatter % arg)
    when Proc
      formatted_args << arg_formatter.call(arg)
    else
      raise "Unknown argument formatter (#{arg_formatter.class}) for #{arg}"
    end
  end
  formatted_args
end

#inspectObject



171
172
173
# File 'lib/avr/opcode.rb', line 171

def inspect
  "#<#{self.class.name} #{mnemonic} #{arg_types}>"
end

#validate(args) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
# File 'lib/avr/opcode.rb', line 141

def validate(args)
  raise IncorrectArgumentCount unless args.size == arg_types.size

  args.each_with_index do |arg, i|
    arg_exception = validate_arg(arg, i)

    raise arg_exception, "Argument #{i} (#{arg}) invalid for #{arg_types[i]}" if arg_exception
  end

  true
end

#validate_arg(arg, arg_number) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/avr/opcode.rb', line 103

def validate_arg(arg, arg_number)
  case arg_types[arg_number]
  when :register
    return RegisterExpected unless arg.is_a?(Register)
  when :word_register
    return WordRegisterExpected unless arg.is_a?(RegisterPair)
  when :byte
    return ByteConstantExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= 0x00 && arg.value <= 0xff
  when :word
    return WordConstantExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= 0x0000 && arg.value <= 0xffff
  when :absolute_pc
    return AbsolutePcExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= 0 && arg.value <= (2**22).to_i - 1
  when :near_relative_pc
    return NearRelativePcExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= -64 && arg.value <= 63
  when :far_relative_pc
    return FarRelativePcExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= -2048 && arg.value <= 2047
  when :io_address
    return IoAddressExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= 0 && arg.value <= 63
  when :lower_io_address
    return IoAddressExpected unless arg.is_a?(Value)
    return ConstantOutOfRange unless arg.value >= 0 && arg.value <= 31
  when :register_with_bit_number
    return RegisterExpected unless arg.register.is_a?(Register)
    return BitNumberExpected unless arg.bit_number.is_a?(Integer)
    return ConstantOutOfRange unless arg.bit_number >= 0 && arg.bit_number <= 7
  when :sreg_flag
    return StatusRegisterBitExpected unless arg.is_a?(Value)
    return StatusRegisterBitExpected unless arg.value == 0 || arg.value == 1
  end
end