rnes 0.1.0
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/.circleci/config.yml +28 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +59 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/rnes +10 -0
- data/lib/rnes.rb +22 -0
- data/lib/rnes/cpu.rb +1058 -0
- data/lib/rnes/cpu_bus.rb +105 -0
- data/lib/rnes/cpu_registers.rb +151 -0
- data/lib/rnes/dma_controller.rb +35 -0
- data/lib/rnes/emulator.rb +61 -0
- data/lib/rnes/errors.rb +46 -0
- data/lib/rnes/image.rb +32 -0
- data/lib/rnes/ines_header.rb +89 -0
- data/lib/rnes/interrupt_line.rb +30 -0
- data/lib/rnes/keypad.rb +51 -0
- data/lib/rnes/logger.rb +142 -0
- data/lib/rnes/operation.rb +52 -0
- data/lib/rnes/operation/records.rb +1477 -0
- data/lib/rnes/parts_factory.rb +99 -0
- data/lib/rnes/ppu.rb +383 -0
- data/lib/rnes/ppu/colors.rb +70 -0
- data/lib/rnes/ppu_bus.rb +60 -0
- data/lib/rnes/ppu_registers.rb +111 -0
- data/lib/rnes/ram.rb +23 -0
- data/lib/rnes/rom.rb +19 -0
- data/lib/rnes/rom_loader.rb +97 -0
- data/lib/rnes/terminal_renderer.rb +59 -0
- data/lib/rnes/version.rb +3 -0
- data/rnes.gemspec +31 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bbefb5781cd92b0dfc9dc54c37bd93927ba138b6
|
4
|
+
data.tar.gz: e7a40524558104975f1fa6c6f4398150e6a6e841
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 12966fa652b5fae1f0815ac80f76f1db5610535c3833f9c760a1bcc8d94d5ff8ded03ab2468a7116e701a05f179d93c1e9184f65e249daad61ffd3591790eff1
|
7
|
+
data.tar.gz: 48143512a1c3206de0dfe00feced98e097a95ed6b4675f8f57fef9fdbf0c7be7dc1462d1eac222a1aeb24a316aa0f8020b6d157889e3840a6335494a4a8749c0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
jobs:
|
2
|
+
rspec:
|
3
|
+
machine: true
|
4
|
+
steps:
|
5
|
+
- checkout
|
6
|
+
- run:
|
7
|
+
command: bundle install
|
8
|
+
name: bundle install
|
9
|
+
- run:
|
10
|
+
command: bundle exec rspec
|
11
|
+
name: rspec
|
12
|
+
rubocop:
|
13
|
+
machine: true
|
14
|
+
steps:
|
15
|
+
- checkout
|
16
|
+
- run:
|
17
|
+
command: bundle install
|
18
|
+
name: bundle install
|
19
|
+
- run:
|
20
|
+
command: bundle exec rubocop
|
21
|
+
name: rubocop
|
22
|
+
version: 2
|
23
|
+
workflows:
|
24
|
+
version: 2
|
25
|
+
test:
|
26
|
+
jobs:
|
27
|
+
- rspec
|
28
|
+
- rubocop
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
Lint/EmptyWhen:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Lint/HandleExceptions:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/BlockLength:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Metrics/BlockNesting:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Metrics/ClassLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/CyclomaticComplexity:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Metrics/LineLength:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Metrics/MethodLength:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Metrics/PerceivedComplexity:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Naming/PredicateName:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/Documentation:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Layout/EmptyLineAfterGuardClause:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Naming/UncommunicativeMethodParamName:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/EmptyMethod:
|
44
|
+
EnforcedStyle: expanded
|
45
|
+
|
46
|
+
Style/GuardClause:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Style/IfUnlessModifier:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/TrailingCommaInArrayLiteral:
|
53
|
+
EnforcedStyleForMultiline: comma
|
54
|
+
|
55
|
+
Style/TrailingCommaInHashLiteral:
|
56
|
+
EnforcedStyleForMultiline: comma
|
57
|
+
|
58
|
+
Style/TrailingCommaInArguments:
|
59
|
+
EnforcedStyleForMultiline: comma
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rnes (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.0)
|
10
|
+
coderay (1.1.2)
|
11
|
+
diff-lcs (1.3)
|
12
|
+
jaro_winkler (1.5.1)
|
13
|
+
method_source (0.9.1)
|
14
|
+
parallel (1.12.1)
|
15
|
+
parser (2.5.3.0)
|
16
|
+
ast (~> 2.4.0)
|
17
|
+
powerpack (0.1.2)
|
18
|
+
pry (0.12.0)
|
19
|
+
coderay (~> 1.1.0)
|
20
|
+
method_source (~> 0.9.0)
|
21
|
+
rainbow (3.0.0)
|
22
|
+
rake (10.5.0)
|
23
|
+
rspec (3.7.0)
|
24
|
+
rspec-core (~> 3.7.0)
|
25
|
+
rspec-expectations (~> 3.7.0)
|
26
|
+
rspec-mocks (~> 3.7.0)
|
27
|
+
rspec-core (3.7.1)
|
28
|
+
rspec-support (~> 3.7.0)
|
29
|
+
rspec-expectations (3.7.0)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.7.0)
|
32
|
+
rspec-mocks (3.7.0)
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
+
rspec-support (~> 3.7.0)
|
35
|
+
rspec-support (3.7.1)
|
36
|
+
rubocop (0.60.0)
|
37
|
+
jaro_winkler (~> 1.5.1)
|
38
|
+
parallel (~> 1.10)
|
39
|
+
parser (>= 2.5, != 2.5.1.1)
|
40
|
+
powerpack (~> 0.1)
|
41
|
+
rainbow (>= 2.2.2, < 4.0)
|
42
|
+
ruby-progressbar (~> 1.7)
|
43
|
+
unicode-display_width (~> 1.4.0)
|
44
|
+
ruby-progressbar (1.10.0)
|
45
|
+
unicode-display_width (1.4.0)
|
46
|
+
|
47
|
+
PLATFORMS
|
48
|
+
ruby
|
49
|
+
|
50
|
+
DEPENDENCIES
|
51
|
+
bundler (~> 1.17)
|
52
|
+
pry
|
53
|
+
rake (~> 10.0)
|
54
|
+
rnes!
|
55
|
+
rspec (= 3.7.0)
|
56
|
+
rubocop (= 0.60.0)
|
57
|
+
|
58
|
+
BUNDLED WITH
|
59
|
+
1.17.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Ryo Nakamura
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Rnes
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/r7kamura/workflows/rnes)
|
4
|
+
[](http://www.rubydoc.info/github/r7kamura/rnes)
|
5
|
+
|
6
|
+
A NES emulator written in Ruby.
|
7
|
+
|
8
|
+
## Requirements
|
9
|
+
|
10
|
+
- Ruby 2.2 or higher
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Install rnes as a gem.
|
15
|
+
|
16
|
+
```sh
|
17
|
+
gem install rnes
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Pass ROM file path to `rnes` executable.
|
23
|
+
|
24
|
+
```sh
|
25
|
+
rnes <path-to-rom-file>
|
26
|
+
```
|
27
|
+
|
28
|
+
## Controls
|
29
|
+
|
30
|
+
```
|
31
|
+
.------------------------------.
|
32
|
+
| | |
|
33
|
+
| _|W|_ .__________________ |
|
34
|
+
| |A( )D| N M , . |
|
35
|
+
| |S| ( _ _ ) (_) (_) |
|
36
|
+
.______________________________.
|
37
|
+
|
38
|
+
Up = W
|
39
|
+
Left = A
|
40
|
+
Down = S
|
41
|
+
Right = D
|
42
|
+
Select = N
|
43
|
+
Start = M
|
44
|
+
B = ,
|
45
|
+
A = .
|
46
|
+
```
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/r7kamura/rnes.
|
51
|
+
|
52
|
+
## License
|
53
|
+
|
54
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'rnes'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/rnes
ADDED
data/lib/rnes.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rnes/cpu'
|
2
|
+
require 'rnes/cpu_bus'
|
3
|
+
require 'rnes/cpu_registers'
|
4
|
+
require 'rnes/dma_controller'
|
5
|
+
require 'rnes/emulator'
|
6
|
+
require 'rnes/errors'
|
7
|
+
require 'rnes/image'
|
8
|
+
require 'rnes/ines_header'
|
9
|
+
require 'rnes/interrupt_line'
|
10
|
+
require 'rnes/keypad'
|
11
|
+
require 'rnes/logger'
|
12
|
+
require 'rnes/operation'
|
13
|
+
require 'rnes/operation/records'
|
14
|
+
require 'rnes/parts_factory'
|
15
|
+
require 'rnes/ppu'
|
16
|
+
require 'rnes/ppu_bus'
|
17
|
+
require 'rnes/ppu/colors'
|
18
|
+
require 'rnes/ram'
|
19
|
+
require 'rnes/rom'
|
20
|
+
require 'rnes/rom_loader'
|
21
|
+
require 'rnes/terminal_renderer'
|
22
|
+
require 'rnes/version'
|
data/lib/rnes/cpu.rb
ADDED
@@ -0,0 +1,1058 @@
|
|
1
|
+
require 'rnes/cpu_bus'
|
2
|
+
require 'rnes/cpu_registers'
|
3
|
+
require 'rnes/errors'
|
4
|
+
require 'rnes/operation'
|
5
|
+
|
6
|
+
module Rnes
|
7
|
+
class Cpu
|
8
|
+
# @return [Rnes::CpuBus] bus
|
9
|
+
attr_reader :bus
|
10
|
+
|
11
|
+
# @return [Rnes::CpuRegisters]
|
12
|
+
attr_reader :registers
|
13
|
+
|
14
|
+
# @param [Rnes::CpuBus] bus
|
15
|
+
# @param [Rnes::InterruptLine] interrupt_line
|
16
|
+
def initialize(bus:, interrupt_line:)
|
17
|
+
@branched = false
|
18
|
+
@bus = bus
|
19
|
+
@interrupt_line = interrupt_line
|
20
|
+
@registers = ::Rnes::CpuRegisters.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# @note For logging.
|
24
|
+
# @return [Rnes::Operation]
|
25
|
+
def read_operation
|
26
|
+
address = @registers.program_counter
|
27
|
+
operation_code = read(address)
|
28
|
+
::Rnes::Operation.build(operation_code)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@registers.reset
|
33
|
+
@registers.program_counter = read_word(0xFFFC)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Integer]
|
37
|
+
def tick
|
38
|
+
handle_interrupts
|
39
|
+
operation = fetch_operation
|
40
|
+
operand = fetch_operand_by(operation.addressing_mode)
|
41
|
+
execute_operation(
|
42
|
+
addressing_mode: operation.addressing_mode,
|
43
|
+
operand: operand,
|
44
|
+
operation_name: operation.name,
|
45
|
+
)
|
46
|
+
@branched = false
|
47
|
+
operation.cycle
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @param [Integer] address
|
53
|
+
def branch(address)
|
54
|
+
@branched = true
|
55
|
+
@registers.program_counter = address
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [Symbol] addressing_mode
|
59
|
+
# @param [Integer, nil] operand
|
60
|
+
# @param [Symbol] operation_name
|
61
|
+
# @return [Integer]
|
62
|
+
def execute_operation(addressing_mode:, operand:, operation_name:)
|
63
|
+
case operation_name
|
64
|
+
when :ADC
|
65
|
+
if addressing_mode == :immediate
|
66
|
+
execute_operation_adc_for_immediate_addressing(operand)
|
67
|
+
else
|
68
|
+
execute_operation_adc_for_non_immediate_addressing(operand)
|
69
|
+
end
|
70
|
+
when :AND
|
71
|
+
if addressing_mode == :immediate
|
72
|
+
execute_operation_and_for_immediate_addressing(operand)
|
73
|
+
else
|
74
|
+
execute_operation_and_for_non_immediate_addressing(operand)
|
75
|
+
end
|
76
|
+
when :ASL
|
77
|
+
if addressing_mode == :accumulator
|
78
|
+
execute_operation_asl_for_accoumulator(operand)
|
79
|
+
else
|
80
|
+
execute_operation_asl_for_non_accumulator(operand)
|
81
|
+
end
|
82
|
+
when :BCC
|
83
|
+
execute_operation_bcc(operand)
|
84
|
+
when :BCS
|
85
|
+
execute_operation_bcs(operand)
|
86
|
+
when :BEQ
|
87
|
+
execute_operation_beq(operand)
|
88
|
+
when :BIT
|
89
|
+
execute_operation_bit(operand)
|
90
|
+
when :BMI
|
91
|
+
execute_operation_bmi(operand)
|
92
|
+
when :BNE
|
93
|
+
execute_operation_bne(operand)
|
94
|
+
when :BPL
|
95
|
+
execute_operation_bpl(operand)
|
96
|
+
when :BRK
|
97
|
+
execute_operation_brk(operand)
|
98
|
+
when :BVC
|
99
|
+
execute_operation_bvc(operand)
|
100
|
+
when :BVS
|
101
|
+
execute_operation_bvs(operand)
|
102
|
+
when :CLC
|
103
|
+
execute_operation_clc(operand)
|
104
|
+
when :CLD
|
105
|
+
execute_operation_cld(operand)
|
106
|
+
when :CLI
|
107
|
+
execute_operation_cli(operand)
|
108
|
+
when :CLV
|
109
|
+
execute_operation_clv(operand)
|
110
|
+
when :CMP
|
111
|
+
if addressing_mode == :immediate
|
112
|
+
execute_operation_cmp_for_immediate_addressing(operand)
|
113
|
+
else
|
114
|
+
execute_operation_cmp_for_non_immediate_addressing(operand)
|
115
|
+
end
|
116
|
+
when :CPX
|
117
|
+
if addressing_mode == :immediate
|
118
|
+
execute_operation_cpx_for_immediate_addressing(operand)
|
119
|
+
else
|
120
|
+
execute_operation_cpx_for_non_immediate_addressing(operand)
|
121
|
+
end
|
122
|
+
when :CPY
|
123
|
+
if addressing_mode == :immediate
|
124
|
+
execute_operation_cpy_for_immediate_addressing(operand)
|
125
|
+
else
|
126
|
+
execute_operation_cpy_for_non_immediate_addressing(operand)
|
127
|
+
end
|
128
|
+
when :DCP
|
129
|
+
execute_operation_dcp(operand)
|
130
|
+
when :DEC
|
131
|
+
execute_operation_dec(operand)
|
132
|
+
when :DEX
|
133
|
+
execute_operation_dex(operand)
|
134
|
+
when :DEY
|
135
|
+
execute_operation_dey(operand)
|
136
|
+
when :EOR
|
137
|
+
if addressing_mode == :immediate
|
138
|
+
execute_operation_eor_for_immediate_addressing(operand)
|
139
|
+
else
|
140
|
+
execute_operation_eor_for_non_immediate_addressing(operand)
|
141
|
+
end
|
142
|
+
when :INC
|
143
|
+
execute_operation_inc(operand)
|
144
|
+
when :INX
|
145
|
+
execute_operation_inx(operand)
|
146
|
+
when :INY
|
147
|
+
execute_operation_iny(operand)
|
148
|
+
when :ISB
|
149
|
+
execute_operation_isb(operand)
|
150
|
+
when :JMP
|
151
|
+
execute_operation_jmp(operand)
|
152
|
+
when :JSR
|
153
|
+
execute_operation_jsr(operand)
|
154
|
+
when :LAX
|
155
|
+
execute_operation_lax(operand)
|
156
|
+
when :LDA
|
157
|
+
if addressing_mode == :immediate
|
158
|
+
execute_operation_lda_for_immediate_addressing(operand)
|
159
|
+
else
|
160
|
+
execute_operation_lda_for_non_immediate_addressing(operand)
|
161
|
+
end
|
162
|
+
when :LDX
|
163
|
+
if addressing_mode == :immediate
|
164
|
+
execute_operation_ldx_for_immediate_addressing(operand)
|
165
|
+
else
|
166
|
+
execute_operation_ldx_for_non_immediate_addressing(operand)
|
167
|
+
end
|
168
|
+
when :LDY
|
169
|
+
if addressing_mode == :immediate
|
170
|
+
execute_operation_ldy_for_immediate_addressing(operand)
|
171
|
+
else
|
172
|
+
execute_operation_ldy_for_non_immediate_addressing(operand)
|
173
|
+
end
|
174
|
+
when :LSR
|
175
|
+
if addressing_mode == :accumulator
|
176
|
+
execute_operation_lsr_for_accumulator(operand)
|
177
|
+
else
|
178
|
+
execute_operation_lsr_for_non_accumulator(operand)
|
179
|
+
end
|
180
|
+
when :NOP
|
181
|
+
execute_operation_nop(operand)
|
182
|
+
when :NOPD
|
183
|
+
execute_operation_nopd(operand)
|
184
|
+
when :NOPI
|
185
|
+
execute_operation_nopi(operand)
|
186
|
+
when :ORA
|
187
|
+
if addressing_mode == :immediate
|
188
|
+
execute_operation_ora_for_immediate_addressing(operand)
|
189
|
+
else
|
190
|
+
execute_operation_ora_for_non_immediate_addressing(operand)
|
191
|
+
end
|
192
|
+
when :PHA
|
193
|
+
execute_operation_pha(operand)
|
194
|
+
when :PHP
|
195
|
+
execute_operation_php(operand)
|
196
|
+
when :PLA
|
197
|
+
execute_operation_pla(operand)
|
198
|
+
when :PLP
|
199
|
+
execute_operation_plp(operand)
|
200
|
+
when :RLA
|
201
|
+
execute_operation_rla(operand)
|
202
|
+
when :ROL
|
203
|
+
if addressing_mode == :accumulator
|
204
|
+
execute_operation_rol_for_accumulator(operand)
|
205
|
+
else
|
206
|
+
execute_operation_rol_for_non_accumulator_(operand)
|
207
|
+
end
|
208
|
+
when :ROR
|
209
|
+
if addressing_mode == :accumulator
|
210
|
+
execute_operation_ror_for_accumulator(operand)
|
211
|
+
else
|
212
|
+
execute_operation_ror_for_non_accumulator(operand)
|
213
|
+
end
|
214
|
+
when :RRA
|
215
|
+
execute_operation_rra(operand)
|
216
|
+
when :RTI
|
217
|
+
execute_operation_rti(operand)
|
218
|
+
when :RTS
|
219
|
+
execute_operation_rts(operand)
|
220
|
+
when :SAX
|
221
|
+
execute_operation_sax(operand)
|
222
|
+
when :SBC
|
223
|
+
if addressing_mode == :immediate
|
224
|
+
execute_operation_sbc_for_immediate_addressing(operand)
|
225
|
+
else
|
226
|
+
execute_operation_sbc_for_non_immediate_addressing(operand)
|
227
|
+
end
|
228
|
+
when :SEC
|
229
|
+
execute_operation_sec(operand)
|
230
|
+
when :SED
|
231
|
+
execute_operation_sed(operand)
|
232
|
+
when :SEI
|
233
|
+
execute_operation_sei(operand)
|
234
|
+
when :SLO
|
235
|
+
execute_operation_slo(operand)
|
236
|
+
when :SRE
|
237
|
+
execute_operation_sre(operand)
|
238
|
+
when :STA
|
239
|
+
execute_operation_sta(operand)
|
240
|
+
when :STX
|
241
|
+
execute_operation_stx(operand)
|
242
|
+
when :STY
|
243
|
+
execute_operation_sty(operand)
|
244
|
+
when :TAX
|
245
|
+
execute_operation_tax(operand)
|
246
|
+
when :TAY
|
247
|
+
execute_operation_tay(operand)
|
248
|
+
when :TSX
|
249
|
+
execute_operation_tsx(operand)
|
250
|
+
when :TXA
|
251
|
+
execute_operation_txa(operand)
|
252
|
+
when :TXS
|
253
|
+
execute_operation_txs(operand)
|
254
|
+
when :TYA
|
255
|
+
execute_operation_tya(operand)
|
256
|
+
else
|
257
|
+
raise ::Rnes::Errors::InvalidOperationError, "Invalid operation: #{operation_name}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# @param [Integer] operand
|
262
|
+
def execute_operation_adc_for_immediate_addressing(operand)
|
263
|
+
result = operand + @registers.accumulator + @registers.carry_bit
|
264
|
+
@registers.carry = result > 0xFF
|
265
|
+
@registers.negative = result[7] == 1
|
266
|
+
@registers.overflow = (@registers.accumulator ^ operand)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
|
267
|
+
@registers.zero = result.zero?
|
268
|
+
@registers.accumulator = result & 0xFF
|
269
|
+
end
|
270
|
+
|
271
|
+
# @param [Integer] operand
|
272
|
+
def execute_operation_adc_for_non_immediate_addressing(operand)
|
273
|
+
operand = read(operand)
|
274
|
+
execute_operation_adc_for_immediate_addressing(operand)
|
275
|
+
end
|
276
|
+
|
277
|
+
# @param [Integer] operand
|
278
|
+
def execute_operation_and_for_immediate_addressing(operand)
|
279
|
+
result = operand & @registers.accumulator
|
280
|
+
@registers.negative = result[7] == 1
|
281
|
+
@registers.zero = result.zero?
|
282
|
+
@registers.accumulator = result
|
283
|
+
end
|
284
|
+
|
285
|
+
# @param [Integer] operand
|
286
|
+
def execute_operation_and_for_non_immediate_addressing(operand)
|
287
|
+
operand = read(operand)
|
288
|
+
execute_operation_and_for_immediate_addressing(operand)
|
289
|
+
end
|
290
|
+
|
291
|
+
# @param [Integer] operand
|
292
|
+
def execute_operation_asl_for_accoumulator(_operand)
|
293
|
+
value = @registers.accumulator
|
294
|
+
result = (value << 1) && 0xFF
|
295
|
+
@registers.carry = value[7] == 1
|
296
|
+
@registers.negative = result[7] == 1
|
297
|
+
@registers.zero = result.zero?
|
298
|
+
@registers.accumulator = result
|
299
|
+
end
|
300
|
+
|
301
|
+
# @param [Integer] operand
|
302
|
+
def execute_operation_asl_for_non_accumulator(operand)
|
303
|
+
value = read(operand)
|
304
|
+
result = (value << 1) && 0xFF
|
305
|
+
@registers.carry = value[7] == 1
|
306
|
+
@registers.negative = result[7] == 1
|
307
|
+
@registers.zero = result.zero?
|
308
|
+
write(operand, result)
|
309
|
+
end
|
310
|
+
|
311
|
+
# @param [Integer] operand
|
312
|
+
def execute_operation_bcc(operand)
|
313
|
+
unless @registers.carry?
|
314
|
+
branch(operand)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# @param [Integer] operand
|
319
|
+
def execute_operation_bcs(operand)
|
320
|
+
if @registers.carry?
|
321
|
+
branch(operand)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# @param [Integer] operand
|
326
|
+
def execute_operation_beq(operand)
|
327
|
+
if @registers.zero?
|
328
|
+
branch(operand)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# @param [Integer] operand
|
333
|
+
def execute_operation_bit(operand)
|
334
|
+
result = read(operand)
|
335
|
+
@registers.overflow = result[6] == 1
|
336
|
+
@registers.negative = result[7] == 1
|
337
|
+
@registers.zero = (@registers.accumulator & result).zero?
|
338
|
+
end
|
339
|
+
|
340
|
+
# @param [Integer] operand
|
341
|
+
def execute_operation_bmi(operand)
|
342
|
+
unless @registers.negative?
|
343
|
+
branch(operand)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# @param [Integer] operand
|
348
|
+
def execute_operation_bne(operand)
|
349
|
+
unless @registers.zero?
|
350
|
+
branch(operand)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# @param [Integer] operand
|
355
|
+
def execute_operation_bpl(operand)
|
356
|
+
unless @registers.negative?
|
357
|
+
branch(operand)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# @param [Integer] operand
|
362
|
+
def execute_operation_brk(_operand)
|
363
|
+
@registers.break = true
|
364
|
+
@registers.program_counter += 1
|
365
|
+
push_word(@registers.program_counter)
|
366
|
+
push(@registers.status)
|
367
|
+
unless @registers.interrupt?
|
368
|
+
@registers.interrupt = true
|
369
|
+
@registers.program_counter = read_word(0xFFFE)
|
370
|
+
end
|
371
|
+
@registers.program_counter -= 1
|
372
|
+
end
|
373
|
+
|
374
|
+
# @param [Integer] operand
|
375
|
+
def execute_operation_bvc(operand)
|
376
|
+
unless @registers.overflow?
|
377
|
+
branch(operand)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# @param [Integer] operand
|
382
|
+
def execute_operation_bvs(operand)
|
383
|
+
if @registers.overflow?
|
384
|
+
branch(operand)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# @param [Integer] operand
|
389
|
+
def execute_operation_clc(_operand)
|
390
|
+
@registers.carry = false
|
391
|
+
end
|
392
|
+
|
393
|
+
# @param [Integer] operand
|
394
|
+
def execute_operation_cld(_operand)
|
395
|
+
@registers.decimal = false
|
396
|
+
end
|
397
|
+
|
398
|
+
# @param [Integer] operand
|
399
|
+
def execute_operation_cli(_operand)
|
400
|
+
@registers.interrupt = false
|
401
|
+
end
|
402
|
+
|
403
|
+
# @param [Integer] operand
|
404
|
+
def execute_operation_clv(_operand)
|
405
|
+
@registers.overflow = false
|
406
|
+
end
|
407
|
+
|
408
|
+
# @param [Integer] operand
|
409
|
+
def execute_operation_cmp_for_immediate_addressing(operand)
|
410
|
+
result = @registers.accumulator - operand
|
411
|
+
@registers.carry = result >= 0
|
412
|
+
@registers.negative = result[7] == 1
|
413
|
+
@registers.zero = (result & 0xFF).zero?
|
414
|
+
end
|
415
|
+
|
416
|
+
# @param [Integer] operand
|
417
|
+
def execute_operation_cmp_for_non_immediate_addressing(operand)
|
418
|
+
operand = read(operand)
|
419
|
+
execute_operation_cmp_for_immediate_addressing(operand)
|
420
|
+
end
|
421
|
+
|
422
|
+
# @param [Integer] operand
|
423
|
+
def execute_operation_cpx_for_immediate_addressing(operand)
|
424
|
+
result = @registers.index_x - operand
|
425
|
+
@registers.carry = result >= 0
|
426
|
+
@registers.negative = result[7] == 1
|
427
|
+
@registers.zero = (result & 0xFF).zero?
|
428
|
+
end
|
429
|
+
|
430
|
+
# @param [Integer] operand
|
431
|
+
def execute_operation_cpx_for_non_immediate_addressing(operand)
|
432
|
+
operand = read(operand)
|
433
|
+
execute_operation_cpx_for_immediate_addressing(operand)
|
434
|
+
end
|
435
|
+
|
436
|
+
# @param [Integer] operand
|
437
|
+
def execute_operation_cpy_for_immediate_addressing(operand)
|
438
|
+
result = @registers.index_y - operand
|
439
|
+
@registers.carry = result >= 0
|
440
|
+
@registers.negative = result[7] == 1
|
441
|
+
@registers.zero = (result & 0xFF).zero?
|
442
|
+
end
|
443
|
+
|
444
|
+
# @param [Integer] operand
|
445
|
+
def execute_operation_cpy_for_non_immediate_addressing(operand)
|
446
|
+
operand = read(operand)
|
447
|
+
execute_operation_cpy_for_immediate_addressing(operand)
|
448
|
+
end
|
449
|
+
|
450
|
+
# @param [Integer] operand
|
451
|
+
def execute_operation_dcp(operand)
|
452
|
+
result = (read(operand) - 1) & 0xFF
|
453
|
+
sub_result = (@registers.accumulator - result) & 0x1FF
|
454
|
+
@registers.negative = sub_result[7] == 1
|
455
|
+
@registers.zero = sub_result.zero?
|
456
|
+
write(operand, result)
|
457
|
+
end
|
458
|
+
|
459
|
+
# @param [Integer] operand
|
460
|
+
def execute_operation_dec(operand)
|
461
|
+
result = (read(operand) - 1) & 0xFF
|
462
|
+
@registers.negative = result[7] == 1
|
463
|
+
@registers.zero = result.zero?
|
464
|
+
write(operand, result)
|
465
|
+
end
|
466
|
+
|
467
|
+
# @param [Integer] operand
|
468
|
+
def execute_operation_dex(_operand)
|
469
|
+
result = (@registers.index_x - 1) & 0xFF
|
470
|
+
@registers.negative = result[7] == 1
|
471
|
+
@registers.zero = result.zero?
|
472
|
+
@registers.index_x = result
|
473
|
+
end
|
474
|
+
|
475
|
+
# @param [Integer] operand
|
476
|
+
def execute_operation_dey(_operand)
|
477
|
+
result = (@registers.index_y - 1) & 0xFF
|
478
|
+
@registers.negative = result[7] == 1
|
479
|
+
@registers.zero = result.zero?
|
480
|
+
@registers.index_y = result
|
481
|
+
end
|
482
|
+
|
483
|
+
# @param [Integer] operand
|
484
|
+
def execute_operation_eor_for_immediate_addressing(operand)
|
485
|
+
result = (operand ^ @registers.accumulator) & 0xFF
|
486
|
+
@registers.negative = result[7] == 1
|
487
|
+
@registers.zero = result.zero?
|
488
|
+
@registers.accumulator = result
|
489
|
+
end
|
490
|
+
|
491
|
+
# @param [Integer] operand
|
492
|
+
def execute_operation_eor_for_non_immediate_addressing(operand)
|
493
|
+
operand = read(operand)
|
494
|
+
execute_operation_eor_for_immediate_addressing(operand)
|
495
|
+
end
|
496
|
+
|
497
|
+
# @param [Integer] operand
|
498
|
+
def execute_operation_inc(operand)
|
499
|
+
result = (read(operand) + 1) & 0xFF
|
500
|
+
@registers.negative = result[7] == 1
|
501
|
+
@registers.zero = result.zero?
|
502
|
+
write(operand, result)
|
503
|
+
end
|
504
|
+
|
505
|
+
# @param [Integer] operand
|
506
|
+
def execute_operation_inx(_operand)
|
507
|
+
result = (@registers.index_x + 1) & 0xFF
|
508
|
+
@registers.negative = result[7] == 1
|
509
|
+
@registers.zero = result.zero?
|
510
|
+
@registers.index_x = result
|
511
|
+
end
|
512
|
+
|
513
|
+
# @param [Integer] operand
|
514
|
+
def execute_operation_iny(_operand)
|
515
|
+
result = (@registers.index_y + 1) & 0xFF
|
516
|
+
@registers.negative = result[7] == 1
|
517
|
+
@registers.zero = result.zero?
|
518
|
+
@registers.index_y = result
|
519
|
+
end
|
520
|
+
|
521
|
+
# @param [Integer] operand
|
522
|
+
def execute_operation_isb(operand)
|
523
|
+
value = (read(operand) + 1) & 0xFF
|
524
|
+
result = (~value & 0xFF) + @registers.accumulator + @registers.carry_bit
|
525
|
+
@registers.overflow = (@registers.accumulator ^ value)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
|
526
|
+
@registers.carry = result > 0xFF
|
527
|
+
@registers.negative = result[7] == 1
|
528
|
+
@registers.zero = result.zero?
|
529
|
+
@registers.accumulator = result & 0xFF
|
530
|
+
write(operand, value)
|
531
|
+
end
|
532
|
+
|
533
|
+
# @param [Integer] operand
|
534
|
+
def execute_operation_jmp(operand)
|
535
|
+
@registers.program_counter = operand
|
536
|
+
end
|
537
|
+
|
538
|
+
# @param [Integer] operand
|
539
|
+
def execute_operation_jsr(operand)
|
540
|
+
push_word(@registers.program_counter - 1)
|
541
|
+
@registers.program_counter = operand
|
542
|
+
end
|
543
|
+
|
544
|
+
# @param [Integer] operand
|
545
|
+
def execute_operation_lax(operand)
|
546
|
+
result = read(operand)
|
547
|
+
@registers.negative = result[7] == 1
|
548
|
+
@registers.zero = result.zero?
|
549
|
+
@registers.accumulator = result
|
550
|
+
@registers.index_x = result
|
551
|
+
end
|
552
|
+
|
553
|
+
# @param [Integer] operand
|
554
|
+
def execute_operation_lda_for_immediate_addressing(operand)
|
555
|
+
@registers.negative = operand[7] == 1
|
556
|
+
@registers.zero = operand.zero?
|
557
|
+
@registers.accumulator = operand
|
558
|
+
end
|
559
|
+
|
560
|
+
# @param [Integer] operand
|
561
|
+
def execute_operation_lda_for_non_immediate_addressing(operand)
|
562
|
+
operand = read(operand)
|
563
|
+
execute_operation_lda_for_immediate_addressing(operand)
|
564
|
+
end
|
565
|
+
|
566
|
+
# @param [Integer] operand
|
567
|
+
def execute_operation_ldx_for_immediate_addressing(operand)
|
568
|
+
@registers.negative = operand[7] == 1
|
569
|
+
@registers.zero = operand.zero?
|
570
|
+
@registers.index_x = operand
|
571
|
+
end
|
572
|
+
|
573
|
+
# @param [Integer] operand
|
574
|
+
def execute_operation_ldx_for_non_immediate_addressing(operand)
|
575
|
+
operand = read(operand)
|
576
|
+
execute_operation_ldx_for_immediate_addressing(operand)
|
577
|
+
end
|
578
|
+
|
579
|
+
# @param [Integer] operand
|
580
|
+
def execute_operation_ldy_for_immediate_addressing(operand)
|
581
|
+
@registers.negative = operand[7] == 1
|
582
|
+
@registers.zero = operand.zero?
|
583
|
+
@registers.index_y = operand
|
584
|
+
end
|
585
|
+
|
586
|
+
# @param [Integer] operand
|
587
|
+
def execute_operation_ldy_for_non_immediate_addressing(operand)
|
588
|
+
operand = read(operand)
|
589
|
+
execute_operation_ldy_for_immediate_addressing(operand)
|
590
|
+
end
|
591
|
+
|
592
|
+
# @param [Integer] operand
|
593
|
+
def execute_operation_lsr_for_accumulator(_operand)
|
594
|
+
value = @registers.accumulator
|
595
|
+
result = value >> 1
|
596
|
+
@registers.carry = value[0] == 1
|
597
|
+
@registers.negative = false
|
598
|
+
@registers.zero = result.zero?
|
599
|
+
@registers.accumulator = result
|
600
|
+
end
|
601
|
+
|
602
|
+
# @param [Integer] operand
|
603
|
+
def execute_operation_lsr_for_non_accumulator(operand)
|
604
|
+
value = read(operand)
|
605
|
+
result = value >> 1
|
606
|
+
@registers.carry = value[0] == 1
|
607
|
+
@registers.negative = false
|
608
|
+
@registers.zero = result.zero?
|
609
|
+
write(operand, result)
|
610
|
+
end
|
611
|
+
|
612
|
+
# @param [Integer] operand
|
613
|
+
def execute_operation_nop(operand)
|
614
|
+
end
|
615
|
+
|
616
|
+
# @param [Integer] operand
|
617
|
+
def execute_operation_nopd(_operand)
|
618
|
+
@registers.program_counter += 1
|
619
|
+
end
|
620
|
+
|
621
|
+
# @param [Integer] operand
|
622
|
+
def execute_operation_nopi(_operand)
|
623
|
+
@registers.program_counter += 2
|
624
|
+
end
|
625
|
+
|
626
|
+
# @param [Integer] operand
|
627
|
+
def execute_operation_ora_for_immediate_addressing(operand)
|
628
|
+
result = @registers.accumulator | operand
|
629
|
+
@registers.negative = result[7] == 1
|
630
|
+
@registers.zero = result.zero?
|
631
|
+
@registers.accumulator = result & 0xFF
|
632
|
+
end
|
633
|
+
|
634
|
+
# @param [Integer] operand
|
635
|
+
def execute_operation_ora_for_non_immediate_addressing(operand)
|
636
|
+
operand = read(operand)
|
637
|
+
execute_operation_ora_for_immediate_addressing(operand)
|
638
|
+
end
|
639
|
+
|
640
|
+
# @param [Integer] operand
|
641
|
+
def execute_operation_pha(_operand)
|
642
|
+
push(@registers.accumulator)
|
643
|
+
end
|
644
|
+
|
645
|
+
# @param [Integer] operand
|
646
|
+
def execute_operation_php(_operand)
|
647
|
+
@registers.break = true
|
648
|
+
push(@registers.status)
|
649
|
+
end
|
650
|
+
|
651
|
+
# @param [Integer] operand
|
652
|
+
def execute_operation_pla(_operand)
|
653
|
+
result = pop
|
654
|
+
@registers.negative = result[7] == 1
|
655
|
+
@registers.zero = result.zero?
|
656
|
+
@registers.accumulator = result
|
657
|
+
end
|
658
|
+
|
659
|
+
# @param [Integer] operand
|
660
|
+
def execute_operation_plp(_operand)
|
661
|
+
@registers.status = pop
|
662
|
+
@registers.reserved = true
|
663
|
+
end
|
664
|
+
|
665
|
+
# @param [Integer] operand
|
666
|
+
def execute_operation_rla(operand)
|
667
|
+
value = (read(operand) << 1) + @registers.carry_bit
|
668
|
+
result = (result & @registers.accumulator) & 0xFF
|
669
|
+
@registers.carry = value[8] == 1
|
670
|
+
@registers.negative = result[7] == 1
|
671
|
+
@registers.zero = result.zero?
|
672
|
+
@registers.accumulator = result
|
673
|
+
write(operand, value)
|
674
|
+
end
|
675
|
+
|
676
|
+
# @param [Integer] operand
|
677
|
+
def execute_operation_rol_for_accumulator(_operand)
|
678
|
+
value = @registers.accumulator
|
679
|
+
result = ((value << 1) | @registers.carry_bit) & 0xFF
|
680
|
+
@registers.carry = value[7] == 1
|
681
|
+
@registers.negative = result[7] == 1
|
682
|
+
@registers.zero = result.zero?
|
683
|
+
@registers.accumulator = result
|
684
|
+
end
|
685
|
+
|
686
|
+
# @param [Integer] operand
|
687
|
+
def execute_operation_rol_for_non_accumulator(operand)
|
688
|
+
value = read(operand)
|
689
|
+
result = ((value << 1) | @registers.carry_bit) & 0xFF
|
690
|
+
@registers.carry = value[7] == 1
|
691
|
+
@registers.negative = result[7] == 1
|
692
|
+
@registers.zero = result.zero?
|
693
|
+
write(operand, result)
|
694
|
+
end
|
695
|
+
|
696
|
+
# @param [Integer] operand
|
697
|
+
def execute_operation_ror_for_accumulator(_operand)
|
698
|
+
value = @registers.accumulator
|
699
|
+
result = ((value >> 1) | (@registers.carry_bit << 7))
|
700
|
+
@registers.carry = value[0] == 1
|
701
|
+
@registers.negative = result[7] == 1
|
702
|
+
@registers.zero = result.zero?
|
703
|
+
@registers.accumulator = result
|
704
|
+
end
|
705
|
+
|
706
|
+
# @param [Integer] operand
|
707
|
+
def execute_operation_ror_for_non_accumulator(operand)
|
708
|
+
value = read(operand)
|
709
|
+
result = ((value >> 1) | (@registers.carry_bit << 7))
|
710
|
+
@registers.carry = value[0] == 1
|
711
|
+
@registers.negative = result[7] == 1
|
712
|
+
@registers.zero = result.zero?
|
713
|
+
write(operand, result)
|
714
|
+
end
|
715
|
+
|
716
|
+
# @param [Integer] operand
|
717
|
+
def execute_operation_rra(operand)
|
718
|
+
read_value = read(operand)
|
719
|
+
value = (read_value >> 1) | (@registers.carry_bit << 7)
|
720
|
+
result = value + @registers.accumulator + read_value[0]
|
721
|
+
@registers.carry = result > 0xFF
|
722
|
+
@registers.overflow = (@registers.accumulator ^ value)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
|
723
|
+
@registers.negative = result[7] == 1
|
724
|
+
@registers.zero = result.zero?
|
725
|
+
write(operand, value)
|
726
|
+
end
|
727
|
+
|
728
|
+
# @param [Integer] operand
|
729
|
+
def execute_operation_rti(_operand)
|
730
|
+
@registers.status = pop
|
731
|
+
@registers.program_counter = pop_word
|
732
|
+
@registers.reserved = true
|
733
|
+
end
|
734
|
+
|
735
|
+
# @param [Integer] operand
|
736
|
+
def execute_operation_rts(_operand)
|
737
|
+
@registers.program_counter = pop_word
|
738
|
+
@registers.program_counter += 1
|
739
|
+
end
|
740
|
+
|
741
|
+
# @param [Integer] operand
|
742
|
+
def execute_operation_sax(operand)
|
743
|
+
result = @registers.accumulator & @registers.index_x
|
744
|
+
write(operand, result)
|
745
|
+
end
|
746
|
+
|
747
|
+
# @param [Integer] operand
|
748
|
+
def execute_operation_sbc_for_immediate_addressing(operand)
|
749
|
+
result = @registers.accumulator - operand - 1 + @registers.carry_bit
|
750
|
+
@registers.overflow = ((@registers.accumulator ^ result) & 0x80 != 0 && ((@registers.accumulator ^ operand) & 0x80) != 0)
|
751
|
+
@registers.carry = result >= 0
|
752
|
+
@registers.negative = result[7] == 1
|
753
|
+
@registers.zero = result.zero?
|
754
|
+
@registers.accumulator = result & 0xFF
|
755
|
+
end
|
756
|
+
|
757
|
+
# @param [Integer] operand
|
758
|
+
def execute_operation_sbc_for_non_immediate_addressing(operand)
|
759
|
+
operand = read(operand)
|
760
|
+
execute_operation_sbc_for_immediate_addressing(operand)
|
761
|
+
end
|
762
|
+
|
763
|
+
# @param [Integer] operand
|
764
|
+
def execute_operation_sec(_operand)
|
765
|
+
@registers.carry = true
|
766
|
+
end
|
767
|
+
|
768
|
+
# @param [Integer] operand
|
769
|
+
def execute_operation_sed(_operand)
|
770
|
+
@registers.decimal = true
|
771
|
+
end
|
772
|
+
|
773
|
+
# @param [Integer] operand
|
774
|
+
def execute_operation_sei(_operand)
|
775
|
+
@registers.interrupt = true
|
776
|
+
end
|
777
|
+
|
778
|
+
# @param [Integer] operand
|
779
|
+
def execute_operation_slo(operand)
|
780
|
+
read_value = read(operand)
|
781
|
+
value = (read_value << 1) & 0xFF
|
782
|
+
result = value | @registers.accumulator
|
783
|
+
@registers.carry = read_value[7] == 1
|
784
|
+
@registers.negative = result == 1
|
785
|
+
@registers.zero = result.zero?
|
786
|
+
@registers.accumulator = result
|
787
|
+
write(operand, value)
|
788
|
+
end
|
789
|
+
|
790
|
+
# @param [Integer] operand
|
791
|
+
def execute_operation_sre(operand)
|
792
|
+
read_value = read(operand)
|
793
|
+
value = read_value >> 1
|
794
|
+
result = value ^ @registers.accumulator
|
795
|
+
@registers.carry = read_value[0] == 1
|
796
|
+
@registers.negative = result[7] == 1
|
797
|
+
@registers.zero = result.zero?
|
798
|
+
@registers.accumulator = result
|
799
|
+
write(operand, value)
|
800
|
+
end
|
801
|
+
|
802
|
+
# @param [Integer] operand
|
803
|
+
def execute_operation_sta(operand)
|
804
|
+
write(operand, @registers.accumulator)
|
805
|
+
end
|
806
|
+
|
807
|
+
# @param [Integer] operand
|
808
|
+
def execute_operation_stx(operand)
|
809
|
+
write(operand, @registers.index_x)
|
810
|
+
end
|
811
|
+
|
812
|
+
# @param [Integer] operand
|
813
|
+
def execute_operation_sty(operand)
|
814
|
+
write(operand, @registers.index_y)
|
815
|
+
end
|
816
|
+
|
817
|
+
# @param [Integer] operand
|
818
|
+
def execute_operation_tax(_operand)
|
819
|
+
result = @registers.accumulator
|
820
|
+
@registers.negative = result[7] == 1
|
821
|
+
@registers.zero = result.zero?
|
822
|
+
@registers.index_x = result
|
823
|
+
end
|
824
|
+
|
825
|
+
# @param [Integer] operand
|
826
|
+
def execute_operation_tay(_operand)
|
827
|
+
result = @registers.accumulator
|
828
|
+
@registers.negative = result[7] == 1
|
829
|
+
@registers.zero = result.zero?
|
830
|
+
@registers.index_y = result
|
831
|
+
end
|
832
|
+
|
833
|
+
# @param [Integer] operand
|
834
|
+
def execute_operation_tsx(_operand)
|
835
|
+
result = @registers.stack_pointer & 0xFF
|
836
|
+
@registers.negative = result[7] == 1
|
837
|
+
@registers.zero = result.zero?
|
838
|
+
@registers.index_x = result
|
839
|
+
end
|
840
|
+
|
841
|
+
# @param [Integer] operand
|
842
|
+
def execute_operation_txa(_operand)
|
843
|
+
result = @registers.index_x
|
844
|
+
@registers.negative = result[7] == 1
|
845
|
+
@registers.zero = result.zero?
|
846
|
+
@registers.accumulator = result
|
847
|
+
end
|
848
|
+
|
849
|
+
# @param [Integer] operand
|
850
|
+
def execute_operation_txs(_operand)
|
851
|
+
@registers.stack_pointer = @registers.index_x + 0x100
|
852
|
+
end
|
853
|
+
|
854
|
+
# @param [Integer] operand
|
855
|
+
def execute_operation_tya(_operand)
|
856
|
+
result = @registers.index_y
|
857
|
+
@registers.negative = result[7] == 1
|
858
|
+
@registers.zero = result.zero?
|
859
|
+
@registers.accumulator = result
|
860
|
+
end
|
861
|
+
|
862
|
+
# @return [Integer]
|
863
|
+
def fetch
|
864
|
+
address = @registers.program_counter
|
865
|
+
value = read(address)
|
866
|
+
@registers.program_counter += 1
|
867
|
+
value
|
868
|
+
end
|
869
|
+
|
870
|
+
# @param [Symbol] addressing_mode
|
871
|
+
def fetch_operand_by(addressing_mode)
|
872
|
+
case addressing_mode
|
873
|
+
when :absolute
|
874
|
+
fetch_operand_by_absolute_addressing
|
875
|
+
when :absolute_x
|
876
|
+
fetch_operand_by_absolute_x_addressing
|
877
|
+
when :absolute_y
|
878
|
+
fetch_operand_by_absolute_y_addressing
|
879
|
+
when :accumulator
|
880
|
+
fetch_operand_by_accumulator_addressing
|
881
|
+
when :immediate
|
882
|
+
fetch_operand_by_immediate_addressing
|
883
|
+
when :implied
|
884
|
+
fetch_operand_by_implied_addressing
|
885
|
+
when :indirect_absolute
|
886
|
+
fetch_operand_by_indirect_absolute_addressing
|
887
|
+
when :post_indexed_indirect
|
888
|
+
fetch_operand_by_post_indexed_indirect_addressing
|
889
|
+
when :pre_indexed_indirect
|
890
|
+
fetch_operand_by_pre_indexed_indirect_addressing
|
891
|
+
when :relative
|
892
|
+
fetch_operand_by_relative_addressing
|
893
|
+
when :zero_page
|
894
|
+
fetch_operand_by_zero_page_addressing
|
895
|
+
when :zero_page_x
|
896
|
+
fetch_operand_by_zero_page_x_addressing
|
897
|
+
when :zero_page_y
|
898
|
+
fetch_operand_by_zero_page_y_addressing
|
899
|
+
else
|
900
|
+
raise ::Rnes::Errors::InvalidAddressingModeError, "Invalid addressing mode: #{addressing_mode}"
|
901
|
+
end
|
902
|
+
end
|
903
|
+
|
904
|
+
# @return [Integer]
|
905
|
+
def fetch_operand_by_absolute_addressing
|
906
|
+
fetch_word
|
907
|
+
end
|
908
|
+
|
909
|
+
# @return [Integer]
|
910
|
+
def fetch_operand_by_absolute_x_addressing
|
911
|
+
(fetch_word + @registers.index_x) & 0xFFFF
|
912
|
+
end
|
913
|
+
|
914
|
+
# @return [Integer]
|
915
|
+
def fetch_operand_by_absolute_y_addressing
|
916
|
+
(fetch_word + @registers.index_y) & 0xFFFF
|
917
|
+
end
|
918
|
+
|
919
|
+
# @return [nil]
|
920
|
+
def fetch_operand_by_accumulator_addressing
|
921
|
+
end
|
922
|
+
|
923
|
+
# @return [Integer]
|
924
|
+
def fetch_operand_by_immediate_addressing
|
925
|
+
fetch
|
926
|
+
end
|
927
|
+
|
928
|
+
# @return [nil]
|
929
|
+
def fetch_operand_by_implied_addressing
|
930
|
+
end
|
931
|
+
|
932
|
+
# @return [Integer]
|
933
|
+
def fetch_operand_by_indirect_absolute_addressing
|
934
|
+
read_word(fetch_word)
|
935
|
+
end
|
936
|
+
|
937
|
+
# @return [Integer]
|
938
|
+
def fetch_operand_by_pre_indexed_indirect_addressing
|
939
|
+
read_word((fetch + @registers.index_x) & 0xFF)
|
940
|
+
end
|
941
|
+
|
942
|
+
# @return [Integer]
|
943
|
+
def fetch_operand_by_post_indexed_indirect_addressing
|
944
|
+
(read_word(fetch) + @registers.index_y) & 0xFF
|
945
|
+
end
|
946
|
+
|
947
|
+
# @return [Integer]
|
948
|
+
def fetch_operand_by_relative_addressing
|
949
|
+
int8 = fetch
|
950
|
+
offset = int8[7] == 1 ? int8 - 256 : int8
|
951
|
+
@registers.program_counter + offset
|
952
|
+
end
|
953
|
+
|
954
|
+
# @return [Integer]
|
955
|
+
def fetch_operand_by_zero_page_addressing
|
956
|
+
fetch
|
957
|
+
end
|
958
|
+
|
959
|
+
# @return [Integer]
|
960
|
+
def fetch_operand_by_zero_page_x_addressing
|
961
|
+
(fetch + @registers.index_x) & 0xFF
|
962
|
+
end
|
963
|
+
|
964
|
+
# @return [Integer]
|
965
|
+
def fetch_operand_by_zero_page_y_addressing
|
966
|
+
(fetch + @registers.index_y) & 0xFF
|
967
|
+
end
|
968
|
+
|
969
|
+
# @return [Rnes::Operation]
|
970
|
+
def fetch_operation
|
971
|
+
operation_code = fetch
|
972
|
+
::Rnes::Operation.build(operation_code)
|
973
|
+
end
|
974
|
+
|
975
|
+
# @return [Integer]
|
976
|
+
def fetch_word
|
977
|
+
fetch | (fetch << 8)
|
978
|
+
end
|
979
|
+
|
980
|
+
def handle_interrupts
|
981
|
+
if @interrupt_line.nmi
|
982
|
+
handle_nmi
|
983
|
+
end
|
984
|
+
if !@registers.interrupt? && @interrupt_line.irq
|
985
|
+
handle_irq
|
986
|
+
end
|
987
|
+
end
|
988
|
+
|
989
|
+
def handle_irq
|
990
|
+
@interrupt_line.deassert_irq
|
991
|
+
@registers.break = false
|
992
|
+
push_word(@registers.program_counter)
|
993
|
+
push(@registers.status)
|
994
|
+
@registers.interrupt = true
|
995
|
+
@registers.program_counter = read_word(0xFFFE)
|
996
|
+
end
|
997
|
+
|
998
|
+
def handle_nmi
|
999
|
+
@interrupt_line.deassert_nmi
|
1000
|
+
@registers.break = false
|
1001
|
+
push_word(@registers.program_counter)
|
1002
|
+
push(@registers.status)
|
1003
|
+
@registers.interrupt = true
|
1004
|
+
@registers.program_counter = read_word(0xFFFA)
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# @return [Integer]
|
1008
|
+
# @raise [Rnes::Errors::StackPointerOverflowError]
|
1009
|
+
def pop
|
1010
|
+
if @registers.stack_pointer < 0x1FF
|
1011
|
+
@registers.stack_pointer += 1
|
1012
|
+
read(@registers.stack_pointer)
|
1013
|
+
else
|
1014
|
+
raise ::Rnes::Errors::StackPointerOverflowError
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# @return [Integer]
|
1019
|
+
def pop_word
|
1020
|
+
pop | pop << 8
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# @param [Integer] value
|
1024
|
+
# @raise [Rnes::Errors::StackPointerOverflowError]
|
1025
|
+
def push(value)
|
1026
|
+
if @registers.stack_pointer > 0x100
|
1027
|
+
write(@registers.stack_pointer, value)
|
1028
|
+
@registers.stack_pointer -= 1
|
1029
|
+
else
|
1030
|
+
raise ::Rnes::Errors::StackPointerOverflowError
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
# @param [Integer] value
|
1035
|
+
def push_word(value)
|
1036
|
+
push(value >> 8)
|
1037
|
+
push(value & 0xFF)
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
# @param [Integer] address
|
1041
|
+
# @return [Integer]
|
1042
|
+
def read(address)
|
1043
|
+
@bus.read(address)
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
# @param [Integer] address
|
1047
|
+
# @return [Integer]
|
1048
|
+
def read_word(address)
|
1049
|
+
read(address) | read((address + 1) & 0xFFFF) << 8
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# @param [Integer] address
|
1053
|
+
# @param [Integer] value
|
1054
|
+
def write(address, value)
|
1055
|
+
@bus.write(address, value)
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
end
|