rnes 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -5
- data/CHANGELOG.md +31 -0
- data/README.md +2 -2
- data/lib/rnes/cpu.rb +51 -25
- data/lib/rnes/cpu_bus.rb +6 -7
- data/lib/rnes/emulator.rb +15 -4
- data/lib/rnes/logger.rb +6 -17
- data/lib/rnes/ppu.rb +131 -91
- data/lib/rnes/ppu_bus.rb +12 -6
- data/lib/rnes/ppu_registers.rb +122 -42
- data/lib/rnes/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 768392aacc5ad611aa059d5ef7a7c23c78ca1088
|
4
|
+
data.tar.gz: 37881bb61c15ed1f35c5015174bf47d26b8fc50f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65ec79252019d26f9d6fa5aa471ecb031adcbeb067c7413a54e0c8dd58a7dbb989d9989638861694caab9ea03dce11625743c0bdcfb05919da3f7c5a9d8b11e0
|
7
|
+
data.tar.gz: 5af737101697e45640a8e211423f5dcbd858c0de846fe2b63d52d197bbd3abdf2baf53d089a3fe7c47440d6f3e6b36bd6597332945ab62a180af62ecd96987ec
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
Layout/EmptyLineAfterGuardClause:
|
2
|
+
Enabled: false
|
3
|
+
|
1
4
|
Lint/EmptyWhen:
|
2
5
|
Enabled: false
|
3
6
|
|
@@ -31,13 +34,10 @@ Metrics/PerceivedComplexity:
|
|
31
34
|
Naming/PredicateName:
|
32
35
|
Enabled: false
|
33
36
|
|
34
|
-
|
35
|
-
Enabled: false
|
36
|
-
|
37
|
-
Layout/EmptyLineAfterGuardClause:
|
37
|
+
Naming/UncommunicativeMethodParamName:
|
38
38
|
Enabled: false
|
39
39
|
|
40
|
-
|
40
|
+
Style/Documentation:
|
41
41
|
Enabled: false
|
42
42
|
|
43
43
|
Style/EmptyMethod:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
## 0.1.1 - 2018-11-12
|
2
|
+
|
3
|
+
### Changed
|
4
|
+
|
5
|
+
- Improve logger format for compatibility with nestest.log.
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- Allow break-less input on key input.
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
- Fix CPU ADC instruction.
|
14
|
+
- Fix CPU ASL instruction.
|
15
|
+
- Fix CPU JMP instruction (for the NMOS 6502 overlap bug).
|
16
|
+
- Fix CPU PHP instruction.
|
17
|
+
- Fix CPU PLP instruction
|
18
|
+
- Fix CPU RLA instruction.
|
19
|
+
- Fix CPU ROL instruction.
|
20
|
+
- Fix CPU SBC instruction.
|
21
|
+
- Fix CPU SLO instruction.
|
22
|
+
- Fix CPU stack pointer behavior on overflow (must be looped, e.g. $100 -> $1FF).
|
23
|
+
- Fix CPU indirect addressing overlap & overflow.
|
24
|
+
- Fix CPU cycle calculation on branch instruction and page cross.
|
25
|
+
- Fix PPU bus mirroring.
|
26
|
+
- Fix PPU sprit hit check.
|
27
|
+
- Fix PPU behaviors after reading $2002.
|
28
|
+
- Fix PPU buffer read on VRAM read from CPU.
|
29
|
+
- Fix PPU sprite RAM read on PPU 0x2004 from CPU.
|
30
|
+
- Fix PPU palette mirroring on $3F10, $3F14, $3F18, and $3F1C.
|
31
|
+
|
1
32
|
## 0.1.0 - 2018-11-09
|
2
33
|
|
3
34
|
### Added
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ A NES emulator written in Ruby.
|
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
14
|
-
Install rnes as a gem.
|
14
|
+
Install rnes as a gem (or clone this repository and run `exe/rnes` executable).
|
15
15
|
|
16
16
|
```sh
|
17
17
|
gem install rnes
|
@@ -22,7 +22,7 @@ gem install rnes
|
|
22
22
|
Pass ROM file path to `rnes` executable.
|
23
23
|
|
24
24
|
```sh
|
25
|
-
rnes <
|
25
|
+
rnes <FILE>
|
26
26
|
```
|
27
27
|
|
28
28
|
## Controls
|
data/lib/rnes/cpu.rb
CHANGED
@@ -16,6 +16,7 @@ module Rnes
|
|
16
16
|
def initialize(bus:, interrupt_line:)
|
17
17
|
@branched = false
|
18
18
|
@bus = bus
|
19
|
+
@crossed = false
|
19
20
|
@interrupt_line = interrupt_line
|
20
21
|
@registers = ::Rnes::CpuRegisters.new
|
21
22
|
end
|
@@ -34,7 +35,7 @@ module Rnes
|
|
34
35
|
end
|
35
36
|
|
36
37
|
# @return [Integer]
|
37
|
-
def
|
38
|
+
def step
|
38
39
|
handle_interrupts
|
39
40
|
operation = fetch_operation
|
40
41
|
operand = fetch_operand_by(operation.addressing_mode)
|
@@ -43,8 +44,10 @@ module Rnes
|
|
43
44
|
operand: operand,
|
44
45
|
operation_name: operation.name,
|
45
46
|
)
|
47
|
+
cycles_count = operation.cycle + (@branched ? 1 : 0) + (@crossed ? 1 : 0)
|
46
48
|
@branched = false
|
47
|
-
|
49
|
+
@crossed = false
|
50
|
+
cycles_count
|
48
51
|
end
|
49
52
|
|
50
53
|
private
|
@@ -203,7 +206,7 @@ module Rnes
|
|
203
206
|
if addressing_mode == :accumulator
|
204
207
|
execute_operation_rol_for_accumulator(operand)
|
205
208
|
else
|
206
|
-
|
209
|
+
execute_operation_rol_for_non_accumulator(operand)
|
207
210
|
end
|
208
211
|
when :ROR
|
209
212
|
if addressing_mode == :accumulator
|
@@ -264,7 +267,7 @@ module Rnes
|
|
264
267
|
@registers.carry = result > 0xFF
|
265
268
|
@registers.negative = result[7] == 1
|
266
269
|
@registers.overflow = (@registers.accumulator ^ operand)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
|
267
|
-
@registers.zero = result.zero?
|
270
|
+
@registers.zero = (result & 0xFF).zero?
|
268
271
|
@registers.accumulator = result & 0xFF
|
269
272
|
end
|
270
273
|
|
@@ -291,7 +294,7 @@ module Rnes
|
|
291
294
|
# @param [Integer] operand
|
292
295
|
def execute_operation_asl_for_accoumulator(_operand)
|
293
296
|
value = @registers.accumulator
|
294
|
-
result = (value << 1)
|
297
|
+
result = (value << 1) & 0xFF
|
295
298
|
@registers.carry = value[7] == 1
|
296
299
|
@registers.negative = result[7] == 1
|
297
300
|
@registers.zero = result.zero?
|
@@ -301,7 +304,7 @@ module Rnes
|
|
301
304
|
# @param [Integer] operand
|
302
305
|
def execute_operation_asl_for_non_accumulator(operand)
|
303
306
|
value = read(operand)
|
304
|
-
result = (value << 1)
|
307
|
+
result = (value << 1) & 0xFF
|
305
308
|
@registers.carry = value[7] == 1
|
306
309
|
@registers.negative = result[7] == 1
|
307
310
|
@registers.zero = result.zero?
|
@@ -339,7 +342,7 @@ module Rnes
|
|
339
342
|
|
340
343
|
# @param [Integer] operand
|
341
344
|
def execute_operation_bmi(operand)
|
342
|
-
|
345
|
+
if @registers.negative?
|
343
346
|
branch(operand)
|
344
347
|
end
|
345
348
|
end
|
@@ -644,8 +647,7 @@ module Rnes
|
|
644
647
|
|
645
648
|
# @param [Integer] operand
|
646
649
|
def execute_operation_php(_operand)
|
647
|
-
@registers.
|
648
|
-
push(@registers.status)
|
650
|
+
push(@registers.status | 0x10)
|
649
651
|
end
|
650
652
|
|
651
653
|
# @param [Integer] operand
|
@@ -658,19 +660,19 @@ module Rnes
|
|
658
660
|
|
659
661
|
# @param [Integer] operand
|
660
662
|
def execute_operation_plp(_operand)
|
661
|
-
@registers.status = pop
|
663
|
+
@registers.status = pop & 0b11101111
|
662
664
|
@registers.reserved = true
|
663
665
|
end
|
664
666
|
|
665
667
|
# @param [Integer] operand
|
666
668
|
def execute_operation_rla(operand)
|
667
669
|
value = (read(operand) << 1) + @registers.carry_bit
|
668
|
-
result = (
|
670
|
+
result = (value & @registers.accumulator) & 0xFF
|
669
671
|
@registers.carry = value[8] == 1
|
670
672
|
@registers.negative = result[7] == 1
|
671
673
|
@registers.zero = result.zero?
|
672
674
|
@registers.accumulator = result
|
673
|
-
write(operand, value)
|
675
|
+
write(operand, value & 0xFF)
|
674
676
|
end
|
675
677
|
|
676
678
|
# @param [Integer] operand
|
@@ -722,6 +724,7 @@ module Rnes
|
|
722
724
|
@registers.overflow = (@registers.accumulator ^ value)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
|
723
725
|
@registers.negative = result[7] == 1
|
724
726
|
@registers.zero = result.zero?
|
727
|
+
@registers.accumulator = result & 0xFF
|
725
728
|
write(operand, value)
|
726
729
|
end
|
727
730
|
|
@@ -750,7 +753,7 @@ module Rnes
|
|
750
753
|
@registers.overflow = ((@registers.accumulator ^ result) & 0x80 != 0 && ((@registers.accumulator ^ operand) & 0x80) != 0)
|
751
754
|
@registers.carry = result >= 0
|
752
755
|
@registers.negative = result[7] == 1
|
753
|
-
@registers.zero = result.zero?
|
756
|
+
@registers.zero = (result & 0xFF).zero?
|
754
757
|
@registers.accumulator = result & 0xFF
|
755
758
|
end
|
756
759
|
|
@@ -781,7 +784,7 @@ module Rnes
|
|
781
784
|
value = (read_value << 1) & 0xFF
|
782
785
|
result = value | @registers.accumulator
|
783
786
|
@registers.carry = read_value[7] == 1
|
784
|
-
@registers.negative = result == 1
|
787
|
+
@registers.negative = result[7] == 1
|
785
788
|
@registers.zero = result.zero?
|
786
789
|
@registers.accumulator = result
|
787
790
|
write(operand, value)
|
@@ -908,12 +911,16 @@ module Rnes
|
|
908
911
|
|
909
912
|
# @return [Integer]
|
910
913
|
def fetch_operand_by_absolute_x_addressing
|
911
|
-
|
914
|
+
base_address = fetch_word
|
915
|
+
@crossed = (base_address & 0xFF00) != ((base_address + @registers.index_x) & 0xFF00)
|
916
|
+
(base_address + @registers.index_x) & 0xFFFF
|
912
917
|
end
|
913
918
|
|
914
919
|
# @return [Integer]
|
915
920
|
def fetch_operand_by_absolute_y_addressing
|
916
|
-
|
921
|
+
base_address = fetch_word
|
922
|
+
@crossed = (base_address & 0xFF00) != ((base_address + @registers.index_y) & 0xFF00)
|
923
|
+
(base_address + @registers.index_y) & 0xFFFF
|
917
924
|
end
|
918
925
|
|
919
926
|
# @return [nil]
|
@@ -929,26 +936,38 @@ module Rnes
|
|
929
936
|
def fetch_operand_by_implied_addressing
|
930
937
|
end
|
931
938
|
|
939
|
+
# @note The address must not overlap a page boundary as a bug in the original 6502 prevents it from being fetched properly.
|
932
940
|
# @return [Integer]
|
933
941
|
def fetch_operand_by_indirect_absolute_addressing
|
934
|
-
|
942
|
+
address = fetch_word
|
943
|
+
low = read(address)
|
944
|
+
high = read((address & 0xFF00) | ((address + 1) & 0xFF))
|
945
|
+
low + (high << 8)
|
935
946
|
end
|
936
947
|
|
937
948
|
# @return [Integer]
|
938
949
|
def fetch_operand_by_pre_indexed_indirect_addressing
|
939
|
-
|
950
|
+
base_address = (fetch + @registers.index_x) & 0xFF
|
951
|
+
address = read_word_with_wrap_around(base_address)
|
952
|
+
@crossed = (address & 0xFF00) != (base_address & 0xFF00)
|
953
|
+
address
|
940
954
|
end
|
941
955
|
|
942
956
|
# @return [Integer]
|
943
957
|
def fetch_operand_by_post_indexed_indirect_addressing
|
944
|
-
|
958
|
+
base_address = fetch
|
959
|
+
address = (read_word_with_wrap_around(base_address) + @registers.index_y) & 0xFFFF
|
960
|
+
@crossed = (address & 0xFF00) != (base_address & 0xFF00)
|
961
|
+
address
|
945
962
|
end
|
946
963
|
|
947
964
|
# @return [Integer]
|
948
965
|
def fetch_operand_by_relative_addressing
|
949
966
|
int8 = fetch
|
950
|
-
offset = int8
|
951
|
-
@registers.program_counter + offset
|
967
|
+
offset = int8 >= 0x80 ? int8 - 256 : int8
|
968
|
+
address = @registers.program_counter + offset
|
969
|
+
@crossed = (address & 0xFF00) != (@registers.program_counter & 0xFF00)
|
970
|
+
address
|
952
971
|
end
|
953
972
|
|
954
973
|
# @return [Integer]
|
@@ -1009,10 +1028,10 @@ module Rnes
|
|
1009
1028
|
def pop
|
1010
1029
|
if @registers.stack_pointer < 0x1FF
|
1011
1030
|
@registers.stack_pointer += 1
|
1012
|
-
read(@registers.stack_pointer)
|
1013
1031
|
else
|
1014
|
-
|
1032
|
+
@registers.stack_pointer = 0x100
|
1015
1033
|
end
|
1034
|
+
read(@registers.stack_pointer)
|
1016
1035
|
end
|
1017
1036
|
|
1018
1037
|
# @return [Integer]
|
@@ -1023,11 +1042,11 @@ module Rnes
|
|
1023
1042
|
# @param [Integer] value
|
1024
1043
|
# @raise [Rnes::Errors::StackPointerOverflowError]
|
1025
1044
|
def push(value)
|
1045
|
+
write(@registers.stack_pointer, value)
|
1026
1046
|
if @registers.stack_pointer > 0x100
|
1027
|
-
write(@registers.stack_pointer, value)
|
1028
1047
|
@registers.stack_pointer -= 1
|
1029
1048
|
else
|
1030
|
-
|
1049
|
+
@registers.stack_pointer = 0x1FF
|
1031
1050
|
end
|
1032
1051
|
end
|
1033
1052
|
|
@@ -1049,6 +1068,13 @@ module Rnes
|
|
1049
1068
|
read(address) | read((address + 1) & 0xFFFF) << 8
|
1050
1069
|
end
|
1051
1070
|
|
1071
|
+
# @param [Integer] byte Unsigned integer from 0x00 to 0xFF.
|
1072
|
+
def read_word_with_wrap_around(byte)
|
1073
|
+
low = read(byte)
|
1074
|
+
high = read((byte + 1) & 0xFF)
|
1075
|
+
low + (high << 8)
|
1076
|
+
end
|
1077
|
+
|
1052
1078
|
# @param [Integer] address
|
1053
1079
|
# @param [Integer] value
|
1054
1080
|
def write(address, value)
|
data/lib/rnes/cpu_bus.rb
CHANGED
@@ -19,7 +19,6 @@ module Rnes
|
|
19
19
|
@ram = ram
|
20
20
|
end
|
21
21
|
|
22
|
-
# @todo
|
23
22
|
# @param [Integer]
|
24
23
|
# @return [Integer]
|
25
24
|
def read(address)
|
@@ -31,7 +30,7 @@ module Rnes
|
|
31
30
|
when 0x2000..0x2007
|
32
31
|
@ppu.read(address - 0x2000)
|
33
32
|
when 0x2008..0x3FFF
|
34
|
-
|
33
|
+
read(address - 0x0008)
|
35
34
|
when 0x4016
|
36
35
|
@keypad1.read
|
37
36
|
when 0x4017
|
@@ -51,7 +50,6 @@ module Rnes
|
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
|
-
# @todo
|
55
53
|
# @param [Integer] address
|
56
54
|
# @param [Integer] value
|
57
55
|
def write(address, value)
|
@@ -63,7 +61,7 @@ module Rnes
|
|
63
61
|
when 0x2000..0x2007
|
64
62
|
@ppu.write(address - 0x2000, value)
|
65
63
|
when 0x2008..0x3FFF
|
66
|
-
|
64
|
+
write(address - 0x0008, value)
|
67
65
|
when 0x4014
|
68
66
|
@dma_controller.request_transfer(address_hint: value)
|
69
67
|
when 0x4016
|
@@ -71,11 +69,12 @@ module Rnes
|
|
71
69
|
when 0x4017
|
72
70
|
@keypad2.write(value)
|
73
71
|
when 0x4000..0x401F
|
74
|
-
|
72
|
+
# TODO: I/O port for APU, etc
|
75
73
|
when 0x4020..0x5FFF
|
76
|
-
|
74
|
+
# TODO: extended RAM on special mappers
|
77
75
|
when 0x6000..0x7FFF
|
78
|
-
|
76
|
+
# TODO: battery-backed-up RAM
|
77
|
+
when 0x8000..0xFFFF
|
79
78
|
else
|
80
79
|
raise ::Rnes::Errors::InvalidCpuBusAddressError, address
|
81
80
|
end
|
data/lib/rnes/emulator.rb
CHANGED
@@ -28,20 +28,23 @@ module Rnes
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def run
|
31
|
+
allow_break_less_input
|
31
32
|
$stdin.noecho do
|
32
33
|
loop do
|
33
34
|
if @logger
|
34
35
|
@logger.puts
|
35
36
|
end
|
36
|
-
|
37
|
+
step
|
37
38
|
end
|
38
39
|
end
|
40
|
+
ensure
|
41
|
+
disallow_break_less_input
|
39
42
|
end
|
40
43
|
|
41
|
-
def
|
44
|
+
def step
|
42
45
|
@dma_controller.transfer_if_requested
|
43
|
-
(@cpu.
|
44
|
-
@ppu.
|
46
|
+
(@cpu.step * 3).times do
|
47
|
+
@ppu.step
|
45
48
|
end
|
46
49
|
@keypad1.check
|
47
50
|
@keypad2.check
|
@@ -49,6 +52,14 @@ module Rnes
|
|
49
52
|
|
50
53
|
private
|
51
54
|
|
55
|
+
def allow_break_less_input
|
56
|
+
`stty -icanon min 1 time 0`
|
57
|
+
end
|
58
|
+
|
59
|
+
def disallow_break_less_input
|
60
|
+
`stty icanon`
|
61
|
+
end
|
62
|
+
|
52
63
|
# @param [Rnes::Rom] from
|
53
64
|
# @param [Rnes::Ram] to
|
54
65
|
def copy(from:, to:)
|
data/lib/rnes/logger.rb
CHANGED
@@ -29,6 +29,7 @@ module Rnes
|
|
29
29
|
'',
|
30
30
|
segment_operation_code,
|
31
31
|
segment_operand,
|
32
|
+
'',
|
32
33
|
segment_operation_full_name,
|
33
34
|
segment_operand_humanized,
|
34
35
|
'',
|
@@ -36,8 +37,6 @@ module Rnes
|
|
36
37
|
segment_cpu_index_x,
|
37
38
|
segment_cpu_index_y,
|
38
39
|
segment_cpu_status,
|
39
|
-
segment_ppu_control1,
|
40
|
-
segment_ppu_control2,
|
41
40
|
segment_cpu_stack_pointer,
|
42
41
|
segment_cycle,
|
43
42
|
segment_ppu_line,
|
@@ -71,12 +70,12 @@ module Rnes
|
|
71
70
|
|
72
71
|
# @return [String]
|
73
72
|
def segment_cpu_status
|
74
|
-
format('P:%
|
73
|
+
format('P:%02X', @cpu.registers.status)
|
75
74
|
end
|
76
75
|
|
77
76
|
# @return [String]
|
78
77
|
def segment_cycle
|
79
|
-
format('CYC:%
|
78
|
+
format('CYC:%3d', @ppu.cycle)
|
80
79
|
end
|
81
80
|
|
82
81
|
# @return [String]
|
@@ -84,9 +83,9 @@ module Rnes
|
|
84
83
|
program_counter = @cpu.registers.program_counter
|
85
84
|
operation = @cpu.read_operation
|
86
85
|
case operation.addressing_mode
|
87
|
-
when :absolute, :absolute_x, :absolute_y, :indirect_absolute
|
86
|
+
when :absolute, :absolute_x, :absolute_y, :indirect_absolute
|
88
87
|
format('%02X %02X', @cpu.bus.read(program_counter + 1), @cpu.bus.read(program_counter + 2))
|
89
|
-
when :immediate, :relative, :zero_page, :zero_page_x, :zero_page_y
|
88
|
+
when :immediate, :relative, :zero_page, :zero_page_x, :zero_page_y, :pre_indexed_indirect, :post_indexed_indirect
|
90
89
|
format('%02X ', @cpu.bus.read(program_counter + 1))
|
91
90
|
else
|
92
91
|
' ' * 5
|
@@ -107,7 +106,7 @@ module Rnes
|
|
107
106
|
''
|
108
107
|
end
|
109
108
|
end
|
110
|
-
format('%-
|
109
|
+
format('%-19s', string)
|
111
110
|
end
|
112
111
|
|
113
112
|
# @return [String]
|
@@ -123,16 +122,6 @@ module Rnes
|
|
123
122
|
format('%-10s', operation.full_name)
|
124
123
|
end
|
125
124
|
|
126
|
-
# @return [String]
|
127
|
-
def segment_ppu_control1
|
128
|
-
format('CTRL1:%08b', @ppu.registers.control1)
|
129
|
-
end
|
130
|
-
|
131
|
-
# @return [String]
|
132
|
-
def segment_ppu_control2
|
133
|
-
format('CTRL2:%08b', @ppu.registers.control2)
|
134
|
-
end
|
135
|
-
|
136
125
|
# @note SL means "Scan Line".
|
137
126
|
# @return [String]
|
138
127
|
def segment_ppu_line
|
data/lib/rnes/ppu.rb
CHANGED
@@ -30,9 +30,15 @@ module Rnes
|
|
30
30
|
|
31
31
|
V_BLANK_HEIGHT = 21
|
32
32
|
|
33
|
-
|
33
|
+
WINDOW_HEIGHT = 240
|
34
34
|
|
35
|
-
|
35
|
+
WINDOW_WIDTH = 256
|
36
|
+
|
37
|
+
TILES_COUNT_IN_HORIZONTAL_LINE = WINDOW_WIDTH / TILE_WIDTH
|
38
|
+
|
39
|
+
TILES_COUNT_IN_VERTICAL_LINE = WINDOW_HEIGHT / TILE_HEIGHT
|
40
|
+
|
41
|
+
TILES_COUNT_IN_WINDOW = TILES_COUNT_IN_HORIZONTAL_LINE * TILES_COUNT_IN_VERTICAL_LINE
|
36
42
|
|
37
43
|
# @note For debug use.
|
38
44
|
# @param [Integer]
|
@@ -58,32 +64,35 @@ module Rnes
|
|
58
64
|
def initialize(bus:, interrupt_line:, renderer:)
|
59
65
|
@bus = bus
|
60
66
|
@cycle = 0
|
61
|
-
@image = ::Rnes::Image.new(height:
|
67
|
+
@image = ::Rnes::Image.new(height: WINDOW_HEIGHT, width: WINDOW_WIDTH)
|
62
68
|
@interrupt_line = interrupt_line
|
63
69
|
@line = 0
|
64
70
|
@registers = ::Rnes::PpuRegisters.new
|
65
71
|
@renderer = renderer
|
66
72
|
@sprite_ram = ::Rnes::Ram.new(bytesize: SPRITE_RAM_BYTESIZE)
|
67
|
-
@
|
68
|
-
@video_ram_address = 0x0000
|
69
|
-
@writing_to_scroll_registers = false
|
70
|
-
@writing_video_ram_address = false
|
73
|
+
@video_ram_reading_buffer = 0x00
|
71
74
|
end
|
72
75
|
|
73
76
|
# @param [Integer] address
|
74
77
|
# @return [Integer]
|
75
78
|
def read(address)
|
76
79
|
case address
|
80
|
+
when 0x0000
|
81
|
+
@registers.control
|
82
|
+
when 0x0001
|
83
|
+
@registers.mask
|
77
84
|
when 0x0002
|
78
|
-
registers.status
|
85
|
+
@registers.status
|
86
|
+
when 0x0004
|
87
|
+
read_from_sprite_ram(@registers.sprite_ram_address)
|
79
88
|
when 0x0007
|
80
|
-
|
89
|
+
read_from_video_ram_for_cpu
|
81
90
|
else
|
82
91
|
raise ::Rnes::Errors::InvalidPpuAddressError, address
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
86
|
-
def
|
95
|
+
def step
|
87
96
|
if on_visible_cycle? && x_in_tile.zero?
|
88
97
|
draw_background_8pixels
|
89
98
|
end
|
@@ -98,6 +107,7 @@ module Rnes
|
|
98
107
|
render_image
|
99
108
|
else
|
100
109
|
self.line += 1
|
110
|
+
check_sprite_hit
|
101
111
|
if on_line_to_start_v_blank?
|
102
112
|
set_v_blank
|
103
113
|
if v_blank_interrupt_enabled?
|
@@ -113,7 +123,7 @@ module Rnes
|
|
113
123
|
# @param [Integer] index
|
114
124
|
# @param [Integer] value
|
115
125
|
def transfer_sprite_data(index:, value:)
|
116
|
-
address = (@sprite_ram_address + index) % SPRITE_RAM_BYTESIZE
|
126
|
+
address = (@registers.sprite_ram_address + index) % SPRITE_RAM_BYTESIZE
|
117
127
|
@sprite_ram.write(address, value)
|
118
128
|
end
|
119
129
|
|
@@ -123,19 +133,19 @@ module Rnes
|
|
123
133
|
def write(address, value)
|
124
134
|
case address
|
125
135
|
when 0x0000
|
126
|
-
registers.
|
136
|
+
@registers.control = value
|
127
137
|
when 0x0001
|
128
|
-
registers.
|
138
|
+
@registers.mask = value
|
129
139
|
when 0x0003
|
130
|
-
|
140
|
+
@registers.sprite_ram_address = value
|
131
141
|
when 0x0004
|
132
|
-
|
142
|
+
write_to_sprite_ram_for_cpu(value)
|
133
143
|
when 0x0005
|
134
|
-
|
144
|
+
@registers.scroll = value
|
135
145
|
when 0x0006
|
136
|
-
|
146
|
+
@registers.video_ram_address = value
|
137
147
|
when 0x0007
|
138
|
-
|
148
|
+
write_to_video_ram_for_cpu(value)
|
139
149
|
else
|
140
150
|
raise ::Rnes::Errors::InvalidPpuAddressError, address
|
141
151
|
end
|
@@ -147,12 +157,41 @@ module Rnes
|
|
147
157
|
@interrupt_line.assert_nmi
|
148
158
|
end
|
149
159
|
|
160
|
+
# @return [Integer]
|
161
|
+
def base_name_table_address
|
162
|
+
ADDRESS_TO_START_NAME_TABLE + @registers.base_name_table_id * 0x400
|
163
|
+
end
|
164
|
+
|
165
|
+
# @return [Integer]
|
166
|
+
def base_background_pattern_table_address
|
167
|
+
if registers.background_pattern_table_address_banked?
|
168
|
+
0x1000
|
169
|
+
else
|
170
|
+
0x0000
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [Integer]
|
175
|
+
def base_sprite_pattern_table_address
|
176
|
+
if registers.sprite_pattern_table_address_banked?
|
177
|
+
0x1000
|
178
|
+
else
|
179
|
+
0x0000
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def check_sprite_hit
|
184
|
+
if read_from_sprite_ram(0) == y && @registers.background_enabled? && @registers.sprite_enabled?
|
185
|
+
registers.sprite_hit = true
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
150
189
|
def clear_sprite_hit
|
151
|
-
registers.
|
190
|
+
registers.sprite_hit = false
|
152
191
|
end
|
153
192
|
|
154
193
|
def clear_v_blank
|
155
|
-
registers.
|
194
|
+
registers.in_v_blank = false
|
156
195
|
end
|
157
196
|
|
158
197
|
def deassert_nmi
|
@@ -160,22 +199,22 @@ module Rnes
|
|
160
199
|
end
|
161
200
|
|
162
201
|
def draw_background_8pixels
|
163
|
-
|
164
|
-
character_index =
|
165
|
-
character_line_low_byte_address = TILE_HEIGHT * 2 * character_index + y_in_tile +
|
166
|
-
character_line_low_byte =
|
167
|
-
character_line_high_byte =
|
202
|
+
base_pattern_table_address = base_background_pattern_table_address
|
203
|
+
character_index = read_character_index(tile_index)
|
204
|
+
character_line_low_byte_address = TILE_HEIGHT * 2 * character_index + y_in_tile + base_pattern_table_address
|
205
|
+
character_line_low_byte = read_character_data(character_line_low_byte_address)
|
206
|
+
character_line_high_byte = read_character_data(character_line_low_byte_address + TILE_HEIGHT)
|
168
207
|
|
169
208
|
block_id = 0
|
170
209
|
block_id |= 0b01 if (x % BLOCK_WIDTH).odd?
|
171
210
|
block_id |= 0b10 if (y % BLOCK_HEIGHT).odd?
|
172
|
-
mini_palette_ids_byte =
|
211
|
+
mini_palette_ids_byte = read_object_attribute(tile_index)
|
173
212
|
mini_palette_id = (mini_palette_ids_byte >> (block_id * 2)) & 0b11
|
174
213
|
|
175
214
|
TILE_WIDTH.times do |x_in_character|
|
176
215
|
index_in_character_line_byte = TILE_WIDTH - 1 - x_in_character
|
177
216
|
background_palette_index = character_line_low_byte[index_in_character_line_byte] | character_line_high_byte[index_in_character_line_byte] << 1 | mini_palette_id << 2
|
178
|
-
color_id =
|
217
|
+
color_id = read_color_id(background_palette_index)
|
179
218
|
@image.write(
|
180
219
|
value: ::Rnes::Ppu::COLORS[color_id],
|
181
220
|
x: x + x_in_character,
|
@@ -198,7 +237,7 @@ module Rnes
|
|
198
237
|
# |`------- horizontal flip
|
199
238
|
# `-------- vertical flip
|
200
239
|
def draw_sprites
|
201
|
-
|
240
|
+
base_pattern_table_address = base_sprite_pattern_table_address
|
202
241
|
SPRITES_COUNT.times do |i|
|
203
242
|
base_sprite_ram_address = i * 4
|
204
243
|
y_for_sprite = (read_from_sprite_ram(base_sprite_ram_address) - TILE_HEIGHT)
|
@@ -207,18 +246,18 @@ module Rnes
|
|
207
246
|
sprite_attribute_byte = read_from_sprite_ram(base_sprite_ram_address + 2)
|
208
247
|
x_for_sprite = read_from_sprite_ram(base_sprite_ram_address + 3)
|
209
248
|
|
210
|
-
character_index =
|
249
|
+
character_index = read_character_index(name_table_index)
|
211
250
|
|
212
251
|
mini_palette_id = sprite_attribute_byte & 0b11
|
213
252
|
|
214
253
|
TILE_HEIGHT.times do |y_in_character|
|
215
|
-
character_line_low_byte_address = TILE_HEIGHT * 2 * character_index + y_in_character +
|
216
|
-
character_line_low_byte =
|
217
|
-
character_line_high_byte =
|
254
|
+
character_line_low_byte_address = TILE_HEIGHT * 2 * character_index + y_in_character + base_pattern_table_address
|
255
|
+
character_line_low_byte = read_character_data(character_line_low_byte_address)
|
256
|
+
character_line_high_byte = read_character_data(character_line_low_byte_address + TILE_HEIGHT)
|
218
257
|
TILE_WIDTH.times do |x_in_character|
|
219
258
|
index_in_character_line_byte = TILE_WIDTH - 1 - x_in_character
|
220
259
|
background_palette_index = character_line_low_byte[index_in_character_line_byte] | character_line_high_byte[index_in_character_line_byte] << 1 | mini_palette_id << 2
|
221
|
-
color_id =
|
260
|
+
color_id = read_color_id(background_palette_index)
|
222
261
|
@image.write(
|
223
262
|
value: ::Rnes::Ppu::COLORS[color_id],
|
224
263
|
x: x_for_sprite + x_in_character,
|
@@ -231,12 +270,12 @@ module Rnes
|
|
231
270
|
|
232
271
|
# @return [Boolean]
|
233
272
|
def on_bottom_end_line?
|
234
|
-
line ==
|
273
|
+
line == WINDOW_HEIGHT + V_BLANK_HEIGHT
|
235
274
|
end
|
236
275
|
|
237
276
|
# @return [Boolean]
|
238
277
|
def on_line_to_start_v_blank?
|
239
|
-
line ==
|
278
|
+
line == WINDOW_HEIGHT
|
240
279
|
end
|
241
280
|
|
242
281
|
# @return [Boolean]
|
@@ -246,44 +285,50 @@ module Rnes
|
|
246
285
|
|
247
286
|
# @return [Boolean]
|
248
287
|
def on_visible_cycle?
|
249
|
-
(0...
|
288
|
+
(0...WINDOW_WIDTH).cover?(x) && (0...WINDOW_HEIGHT).cover?(y)
|
250
289
|
end
|
251
290
|
|
252
291
|
# @param [Integer] index
|
253
|
-
# @return [Integer]
|
254
|
-
def
|
255
|
-
@bus.read(
|
292
|
+
# @return [Integer]
|
293
|
+
def read_character_data(index)
|
294
|
+
@bus.read(index)
|
256
295
|
end
|
257
296
|
|
258
297
|
# @param [Integer] index
|
259
298
|
# @return [Integer]
|
260
|
-
def
|
261
|
-
@bus.read(
|
299
|
+
def read_character_index(index)
|
300
|
+
@bus.read(base_name_table_address + index)
|
262
301
|
end
|
263
302
|
|
264
303
|
# @param [Integer] index
|
265
304
|
# @return [Integer]
|
266
|
-
def
|
305
|
+
def read_color_id(index)
|
267
306
|
@bus.read(ADDRESS_TO_START_BACKGROUND_PALETTE_TABLE + index)
|
268
307
|
end
|
269
308
|
|
270
|
-
# @param [Integer]
|
309
|
+
# @param [Integer] address
|
271
310
|
# @return [Integer]
|
272
|
-
def
|
273
|
-
@
|
311
|
+
def read_from_sprite_ram(address)
|
312
|
+
@sprite_ram.read(address)
|
274
313
|
end
|
275
314
|
|
276
|
-
# @
|
277
|
-
|
278
|
-
|
279
|
-
@
|
280
|
-
value
|
315
|
+
# @param [Integer] index
|
316
|
+
# @return [Integer] 4-color-palette IDs of 4 blocks, as 8 bit data.
|
317
|
+
def read_object_attribute(index)
|
318
|
+
@bus.read(ADDRESS_TO_START_ATTRIBUTE_TABLE + index)
|
281
319
|
end
|
282
320
|
|
283
|
-
# @param [Integer] address
|
284
321
|
# @return [Integer]
|
285
|
-
def
|
286
|
-
@
|
322
|
+
def read_from_video_ram_for_cpu
|
323
|
+
if (0x3F00..0x3F1F).cover?(@registers.video_ram_address % 0x4000)
|
324
|
+
value = @bus.read(@registers.video_ram_address)
|
325
|
+
@video_ram_reading_buffer = @bus.read(@registers.video_ram_address - 0x1000)
|
326
|
+
else
|
327
|
+
value = @video_ram_reading_buffer
|
328
|
+
@video_ram_reading_buffer = @bus.read(@registers.video_ram_address)
|
329
|
+
end
|
330
|
+
@registers.increment_video_ram_address(video_ram_address_offset)
|
331
|
+
value
|
287
332
|
end
|
288
333
|
|
289
334
|
def render_image
|
@@ -291,12 +336,32 @@ module Rnes
|
|
291
336
|
end
|
292
337
|
|
293
338
|
def set_v_blank
|
294
|
-
registers.
|
339
|
+
registers.in_v_blank = true
|
295
340
|
end
|
296
341
|
|
297
|
-
#
|
342
|
+
# +-----------+-----------+
|
343
|
+
# | 0(0x0000) | 1(0x0400) |
|
344
|
+
# +-----------+-----------+
|
345
|
+
# | 2(0x0800) | 3(0x0C00) |
|
346
|
+
# +-----------+-----------+
|
347
|
+
# @return [Integer] Integer from 0x0000 to 0x0FC0.
|
298
348
|
def tile_index
|
299
|
-
|
349
|
+
tile_index_in_window + tile_index_paging_offset
|
350
|
+
end
|
351
|
+
|
352
|
+
# @return [Integer] Integer from 0x0000 to 0x03C0.
|
353
|
+
def tile_index_in_window
|
354
|
+
(y_of_tile % TILES_COUNT_IN_VERTICAL_LINE) * TILES_COUNT_IN_HORIZONTAL_LINE + x_of_tile % TILES_COUNT_IN_HORIZONTAL_LINE
|
355
|
+
end
|
356
|
+
|
357
|
+
# @return [Integer] Integer from 0 to 3.
|
358
|
+
def tile_index_page
|
359
|
+
x_of_tile / TILES_COUNT_IN_HORIZONTAL_LINE + y_of_tile / TILES_COUNT_IN_VERTICAL_LINE * 2
|
360
|
+
end
|
361
|
+
|
362
|
+
# @return [Integer] 0x0000, 0x0400, 0x0800, or 0x0C00.
|
363
|
+
def tile_index_paging_offset
|
364
|
+
tile_index_page * 0x0400
|
300
365
|
end
|
301
366
|
|
302
367
|
# @return [Boolean]
|
@@ -306,48 +371,23 @@ module Rnes
|
|
306
371
|
|
307
372
|
# @return [Integer]
|
308
373
|
def video_ram_address_offset
|
309
|
-
if registers.
|
310
|
-
|
374
|
+
if registers.horizontal_increment?
|
375
|
+
TILES_COUNT_IN_HORIZONTAL_LINE
|
311
376
|
else
|
312
377
|
1
|
313
378
|
end
|
314
379
|
end
|
315
380
|
|
316
|
-
# @param [Integer] address
|
317
|
-
def write_sprite_ram_address(address)
|
318
|
-
@sprite_ram_address = address
|
319
|
-
end
|
320
|
-
|
321
381
|
# @param [Integer] value
|
322
|
-
def
|
323
|
-
|
324
|
-
|
325
|
-
else
|
326
|
-
@registers.scroll_horizontal = value
|
327
|
-
end
|
328
|
-
@writing_to_scroll_registers = !@writing_to_scroll_registers
|
329
|
-
end
|
330
|
-
|
331
|
-
# @param [Integer] value
|
332
|
-
def write_to_sprite_ram(value)
|
333
|
-
@sprite_ram.write(@sprite_ram_address, value)
|
334
|
-
@sprite_ram_address += 1
|
335
|
-
end
|
336
|
-
|
337
|
-
# @param [Integer] address
|
338
|
-
def write_video_ram_address(address)
|
339
|
-
if @writing_video_ram_address
|
340
|
-
@video_ram_address |= address
|
341
|
-
else
|
342
|
-
@video_ram_address = address << 8
|
343
|
-
end
|
344
|
-
@writing_video_ram_address = !@writing_video_ram_address
|
382
|
+
def write_to_sprite_ram_for_cpu(value)
|
383
|
+
@sprite_ram.write(@registers.sprite_ram_address, value)
|
384
|
+
@registers.sprite_ram_address += 1
|
345
385
|
end
|
346
386
|
|
347
387
|
# @param [Integer] value
|
348
|
-
def
|
349
|
-
@bus.write(@video_ram_address, value)
|
350
|
-
@
|
388
|
+
def write_to_video_ram_for_cpu(value)
|
389
|
+
@bus.write(@registers.video_ram_address, value)
|
390
|
+
@registers.increment_video_ram_address(video_ram_address_offset)
|
351
391
|
end
|
352
392
|
|
353
393
|
# @return [Integer]
|
@@ -362,7 +402,7 @@ module Rnes
|
|
362
402
|
|
363
403
|
# @return [Integer]
|
364
404
|
def x_of_tile
|
365
|
-
x / TILE_WIDTH
|
405
|
+
(x + @registers.scroll_x) / TILE_WIDTH
|
366
406
|
end
|
367
407
|
|
368
408
|
# @return [Integer]
|
@@ -377,7 +417,7 @@ module Rnes
|
|
377
417
|
|
378
418
|
# @return [Integer]
|
379
419
|
def y_of_tile
|
380
|
-
y / TILE_HEIGHT
|
420
|
+
(y + @registers.scroll_y) / TILE_HEIGHT
|
381
421
|
end
|
382
422
|
end
|
383
423
|
end
|
data/lib/rnes/ppu_bus.rb
CHANGED
@@ -25,12 +25,14 @@ module Rnes
|
|
25
25
|
read(address - 0x0800)
|
26
26
|
when 0x3000..0x3EFF
|
27
27
|
read(address - 0x1000)
|
28
|
+
when 0x3F10, 0x3F14, 0x3F18, 0x3F1C
|
29
|
+
read(address - 0x0010)
|
28
30
|
when 0x3F00..0x3F1F
|
29
31
|
@video_ram.read(address - 0x2000)
|
30
32
|
when 0x3F20..0x3FFF
|
31
|
-
read(address -
|
33
|
+
read(address - 0x0020)
|
32
34
|
when 0x4000..0xFFFF
|
33
|
-
read(address - 0x4000)
|
35
|
+
read(address - 0x4000)
|
34
36
|
else
|
35
37
|
raise ::Rnes::Errors::InvalidPpuBusAddressError, address
|
36
38
|
end
|
@@ -45,13 +47,17 @@ module Rnes
|
|
45
47
|
when 0x2000..0x27FF
|
46
48
|
@video_ram.write(address - 0x2000, value)
|
47
49
|
when 0x2800..0x2FFF
|
48
|
-
|
50
|
+
write(address - 0x0800, value)
|
49
51
|
when 0x3000..0x3EFF
|
50
|
-
|
52
|
+
write(address - 0x1000, value)
|
53
|
+
when 0x3F10, 0x3F14, 0x3F18, 0x3F1C
|
54
|
+
write(address - 0x0010, value)
|
51
55
|
when 0x3F00..0x3F1F
|
52
56
|
@video_ram.write(address - 0x2000, value)
|
53
|
-
when 0x3F00..
|
54
|
-
write(address -
|
57
|
+
when 0x3F00..0x3FFF
|
58
|
+
write(address - 0x0020, value)
|
59
|
+
when 0x4000..0xFFFF
|
60
|
+
write(address - 0x4000, value)
|
55
61
|
else
|
56
62
|
raise ::Rnes::Errors::InvalidPpuBusAddressError, address
|
57
63
|
end
|
data/lib/rnes/ppu_registers.rb
CHANGED
@@ -1,101 +1,181 @@
|
|
1
1
|
module Rnes
|
2
2
|
class PpuRegisters
|
3
3
|
STATUS_IN_V_BLANK_BIT_INDEX = 7
|
4
|
-
STATUS_SPRITE_HIT_BIT_INDEX =
|
4
|
+
STATUS_SPRITE_HIT_BIT_INDEX = 6
|
5
|
+
STATUS_OVERFLOW_BIT_INDEX = 5
|
5
6
|
|
6
7
|
# @param [Integer]
|
7
8
|
# @return [Integer]
|
8
|
-
attr_accessor :
|
9
|
+
attr_accessor :control
|
9
10
|
|
10
11
|
# @param [Integer]
|
11
12
|
# @return [Integer]
|
12
|
-
attr_accessor :
|
13
|
+
attr_accessor :mask
|
13
14
|
|
14
|
-
# @param [Integer]
|
15
15
|
# @return [Integer]
|
16
|
-
attr_accessor :
|
16
|
+
attr_accessor :sprite_ram_address
|
17
17
|
|
18
|
-
# @param [Integer]
|
19
18
|
# @return [Integer]
|
20
|
-
|
19
|
+
attr_reader :scroll_x
|
20
|
+
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :scroll_y
|
21
23
|
|
22
|
-
# @param [Integer]
|
23
24
|
# @return [Integer]
|
24
|
-
|
25
|
+
attr_reader :video_ram_address
|
26
|
+
|
27
|
+
# @param [Integer]
|
28
|
+
attr_writer :status
|
25
29
|
|
26
30
|
def initialize
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@scroll_horizontal = 0x0
|
30
|
-
@scroll_vertical = 0x0
|
31
|
+
@control = 0x0
|
32
|
+
@mask = 0x0
|
31
33
|
@status = 0x0
|
34
|
+
|
35
|
+
@scroll_x = 0x0
|
36
|
+
@scroll_y = 0x0
|
37
|
+
|
38
|
+
@sprite_ram_address = 0x00
|
39
|
+
@video_ram_address = 0x0000
|
40
|
+
|
41
|
+
@address_latch = false
|
42
|
+
@scroll_latch = false
|
32
43
|
end
|
33
44
|
|
34
45
|
# @return [Boolean]
|
35
|
-
def
|
36
|
-
@
|
46
|
+
def background_enabled?
|
47
|
+
@mask[3] == 1
|
37
48
|
end
|
38
49
|
|
39
50
|
# @return [Boolean]
|
40
|
-
def
|
41
|
-
@
|
51
|
+
def background_pattern_table_address_banked?
|
52
|
+
@control[4] == 1
|
42
53
|
end
|
43
54
|
|
44
|
-
#
|
45
|
-
|
46
|
-
|
55
|
+
# +------------+------------|
|
56
|
+
# | 0 (0x2000) | 1 (0x2400) |
|
57
|
+
# +------------+------------|
|
58
|
+
# | 2 (0x2800) | 3 (0x2C00) |
|
59
|
+
# +------------+------------|
|
60
|
+
# @return [Integer] An integer from 0 to 3.
|
61
|
+
def base_name_table_id
|
62
|
+
@control & 0b11
|
47
63
|
end
|
48
64
|
|
49
65
|
# @return [Boolean]
|
50
|
-
def
|
51
|
-
@
|
66
|
+
def color_blue_emphasized?
|
67
|
+
@mask[7] == 1
|
52
68
|
end
|
53
69
|
|
54
70
|
# @return [Boolean]
|
55
|
-
def
|
56
|
-
@
|
71
|
+
def color_green_emphasized?
|
72
|
+
@mask[6] == 1
|
57
73
|
end
|
58
74
|
|
59
75
|
# @return [Boolean]
|
60
|
-
def
|
61
|
-
@
|
76
|
+
def color_red_emphasized?
|
77
|
+
@mask[5] == 1
|
62
78
|
end
|
63
79
|
|
64
80
|
# @return [Boolean]
|
65
|
-
def
|
66
|
-
@
|
81
|
+
def color_greyscaled?
|
82
|
+
@mask[0] == 1
|
67
83
|
end
|
68
84
|
|
69
85
|
# @return [Boolean]
|
70
86
|
def has_v_blank_irq_enabled_bit?
|
71
|
-
@
|
87
|
+
@control[7] == 1
|
72
88
|
end
|
73
89
|
|
74
|
-
#
|
75
|
-
|
76
|
-
|
77
|
-
# +------------+------------|
|
78
|
-
# | 2 (0x2800) | 3 (0x2C00) |
|
79
|
-
# +------------+------------|
|
80
|
-
# @return [Integer] An integer from 0 to 3.
|
81
|
-
def name_table_id
|
82
|
-
@status & 0b11
|
90
|
+
# @return [Boolean]
|
91
|
+
def horizontal_increment?
|
92
|
+
@control[2] == 1
|
83
93
|
end
|
84
94
|
|
85
|
-
|
86
|
-
|
95
|
+
# @return [Boolean]
|
96
|
+
def in_v_blank?
|
97
|
+
@status[STATUS_IN_V_BLANK_BIT_INDEX] == 1
|
87
98
|
end
|
88
99
|
|
89
100
|
# @param [Boolean] boolean
|
90
|
-
def
|
101
|
+
def in_v_blank=(boolean)
|
91
102
|
toggle_status_bit(STATUS_IN_V_BLANK_BIT_INDEX, boolean)
|
92
103
|
end
|
93
104
|
|
105
|
+
# @param [Integer] offset
|
106
|
+
def increment_video_ram_address(offset)
|
107
|
+
@video_ram_address += offset
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Boolean]
|
111
|
+
def leftmost_background_shown?
|
112
|
+
@mask[1] == 1
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [Boolean]
|
116
|
+
def leftmost_sprite_shown?
|
117
|
+
@mask[2] == 1
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param [Boolean] boolean
|
121
|
+
def overflow=(boolean)
|
122
|
+
toggle_status_bit(STATUS_OVERFLOW_BIT_INDEX, boolean)
|
123
|
+
end
|
124
|
+
|
125
|
+
# @param [Integer] value
|
126
|
+
def scroll=(value)
|
127
|
+
if @scroll_latch
|
128
|
+
@scroll_y = value
|
129
|
+
else
|
130
|
+
@scroll_x = value
|
131
|
+
end
|
132
|
+
@scroll_latch = !@scroll_latch
|
133
|
+
end
|
134
|
+
|
135
|
+
# @return [Boolean]
|
136
|
+
def sprite_enabled?
|
137
|
+
@mask[4] == 1
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Boolean]
|
141
|
+
def sprite_hit?
|
142
|
+
@status[STATUS_SPRITE_HIT_BIT_INDEX] == STATUS_SPRITE_HIT_BIT_INDEX
|
143
|
+
end
|
144
|
+
|
94
145
|
# @param [Boolean] boolean
|
95
|
-
def
|
146
|
+
def sprite_hit=(boolean)
|
96
147
|
toggle_status_bit(STATUS_SPRITE_HIT_BIT_INDEX, boolean)
|
97
148
|
end
|
98
149
|
|
150
|
+
# @return [Boolean]
|
151
|
+
def sprite_pattern_table_address_banked?
|
152
|
+
@control[3] == 1
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [Boolean]
|
156
|
+
def sprite_size_doubled?
|
157
|
+
@control[4] == 1
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [Integer]
|
161
|
+
def status
|
162
|
+
value = @status
|
163
|
+
self.in_v_blank = false
|
164
|
+
@address_latch = false
|
165
|
+
@scroll_latch = false
|
166
|
+
value
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param [Integer] value
|
170
|
+
def video_ram_address=(value)
|
171
|
+
if @address_latch
|
172
|
+
@video_ram_address |= value
|
173
|
+
else
|
174
|
+
@video_ram_address = value << 8
|
175
|
+
end
|
176
|
+
@address_latch = !@address_latch
|
177
|
+
end
|
178
|
+
|
99
179
|
private
|
100
180
|
|
101
181
|
# @param [Integer] index
|
data/lib/rnes/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rnes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|