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.
@@ -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
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /rnes.log
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -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
@@ -0,0 +1,14 @@
1
+ ## 0.1.0 - 2018-11-09
2
+
3
+ ### Added
4
+
5
+ - Add iNES header parser.
6
+ - Add ROM loader.
7
+ - Add CPU.
8
+ - Add PPU.
9
+ - Add terminal renderer.
10
+ - Add character RAM.
11
+ - Add DMA.
12
+ - Add logger for debug use.
13
+ - Add NMI interruption.
14
+ - Add keypad.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in rnes.gemspec
6
+ gemspec
@@ -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
@@ -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.
@@ -0,0 +1,54 @@
1
+ # Rnes
2
+
3
+ [![CircleCI](https://circleci.com/gh/r7kamura/rnes.svg?style=svg)](https://circleci.com/gh/r7kamura/workflows/rnes)
4
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](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).
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -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__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
3
+ require 'rnes'
4
+
5
+ path = ARGV[0]
6
+ io = File.binread(path)
7
+ bytes = io.bytes
8
+ emulator = Rnes::Emulator.new
9
+ emulator.load_rom(bytes)
10
+ emulator.run
@@ -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'
@@ -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