rnes 0.1.0

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