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.
- checksums.yaml +7 -0
- data/LICENSE.md +28 -0
- data/README.md +17 -0
- data/bin/avruby_shell +162 -0
- data/lib/avr.rb +41 -0
- data/lib/avr/argument.rb +23 -0
- data/lib/avr/clock.rb +99 -0
- data/lib/avr/cpu.rb +254 -0
- data/lib/avr/device.rb +249 -0
- data/lib/avr/device/atmel_atmega328p.rb +181 -0
- data/lib/avr/instruction.rb +72 -0
- data/lib/avr/memory.rb +163 -0
- data/lib/avr/memory/eeprom.rb +65 -0
- data/lib/avr/memory/flash.rb +13 -0
- data/lib/avr/memory/memory_byte.rb +54 -0
- data/lib/avr/memory/sram.rb +13 -0
- data/lib/avr/opcode.rb +237 -0
- data/lib/avr/opcode/branch/conditional.rb +61 -0
- data/lib/avr/opcode/branch/return.rb +23 -0
- data/lib/avr/opcode/branch/unconditional.rb +74 -0
- data/lib/avr/opcode/break.rb +14 -0
- data/lib/avr/opcode/compare.rb +66 -0
- data/lib/avr/opcode/data/immediate.rb +14 -0
- data/lib/avr/opcode/data/program.rb +25 -0
- data/lib/avr/opcode/data/sram.rb +222 -0
- data/lib/avr/opcode/data/stack.rb +22 -0
- data/lib/avr/opcode/io/bit.rb +22 -0
- data/lib/avr/opcode/io/in_out.rb +38 -0
- data/lib/avr/opcode/math/addition.rb +120 -0
- data/lib/avr/opcode/math/bitwise.rb +170 -0
- data/lib/avr/opcode/math/multiplication.rb +23 -0
- data/lib/avr/opcode/math/subtraction.rb +137 -0
- data/lib/avr/opcode/nop.rb +14 -0
- data/lib/avr/opcode/opcodes.rb +24 -0
- data/lib/avr/opcode/operand_parsers.rb +75 -0
- data/lib/avr/opcode/register.rb +37 -0
- data/lib/avr/opcode/sleep.rb +14 -0
- data/lib/avr/opcode/sreg.rb +56 -0
- data/lib/avr/opcode/wdr.rb +14 -0
- data/lib/avr/opcode_decoder.rb +202 -0
- data/lib/avr/oscillator.rb +33 -0
- data/lib/avr/port.rb +107 -0
- data/lib/avr/register.rb +18 -0
- data/lib/avr/register/memory_byte_register.rb +27 -0
- data/lib/avr/register/memory_byte_register_with_named_bits.rb +85 -0
- data/lib/avr/register/register_file.rb +59 -0
- data/lib/avr/register/register_pair.rb +46 -0
- data/lib/avr/register/sp.rb +41 -0
- data/lib/avr/register/sreg.rb +12 -0
- data/lib/avr/register_with_bit_number.rb +40 -0
- data/lib/avr/register_with_displacement.rb +31 -0
- data/lib/avr/register_with_modification.rb +35 -0
- data/lib/avr/register_with_named_bit.rb +36 -0
- data/lib/avr/value.rb +45 -0
- data/lib/avr/version.rb +6 -0
- metadata +182 -0
data/lib/avr/memory.rb
ADDED
@@ -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,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
|
data/lib/avr/opcode.rb
ADDED
@@ -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
|