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