rubyboy 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +34 -8
- data/CHANGELOG.md +11 -0
- data/Rakefile +3 -3
- data/lib/opcodes.json +11657 -0
- data/lib/roms/cpu_instrs/cpu_instrs.gb +0 -0
- data/lib/roms/{hello.gb → cpu_instrs/individual/01-special.gb} +0 -0
- data/lib/roms/cpu_instrs/individual/02-interrupts.gb +0 -0
- data/lib/roms/cpu_instrs/individual/03-op sp,hl.gb +0 -0
- data/lib/roms/cpu_instrs/individual/04-op r,imm.gb +0 -0
- data/lib/roms/cpu_instrs/individual/05-op rp.gb +0 -0
- data/lib/roms/cpu_instrs/individual/06-ld r,r.gb +0 -0
- data/lib/roms/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb +0 -0
- data/lib/roms/cpu_instrs/individual/08-misc instrs.gb +0 -0
- data/lib/roms/cpu_instrs/individual/09-op r,r.gb +0 -0
- data/lib/roms/cpu_instrs/individual/10-bit ops.gb +0 -0
- data/lib/roms/cpu_instrs/individual/11-op a,(hl).gb +0 -0
- data/lib/roms/cpu_instrs/readme.txt +119 -0
- data/lib/roms/instr_timing/instr_timing.gb +0 -0
- data/lib/roms/instr_timing/readme.txt +139 -0
- data/lib/rubyboy/bus.rb +150 -11
- data/lib/rubyboy/cartridge/factory.rb +21 -0
- data/lib/rubyboy/cartridge/mbc1.rb +71 -0
- data/lib/rubyboy/cartridge/nombc.rb +19 -0
- data/lib/rubyboy/cpu.rb +1663 -110
- data/lib/rubyboy/interrupt.rb +22 -0
- data/lib/rubyboy/ppu.rb +15 -15
- data/lib/rubyboy/ram.rb +14 -0
- data/lib/rubyboy/register.rb +26 -0
- data/lib/rubyboy/rom.rb +1 -1
- data/lib/rubyboy/timer.rb +68 -0
- data/lib/rubyboy/version.rb +1 -1
- data/lib/rubyboy.rb +14 -12
- metadata +27 -4
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Game Boy CPU Instruction Behavior Test
|
|
2
|
+
--------------------------------------
|
|
3
|
+
This ROM tests the behavior of all CPU instructions except STOP and the
|
|
4
|
+
11 illegal opcodes. The tests are fairly thorough, running instructions
|
|
5
|
+
with boundary data and verifying both the result and that other
|
|
6
|
+
registers are not modified. Instructions which perform the same
|
|
7
|
+
operation on different registers are each tested just as thoroughly, in
|
|
8
|
+
case an emulator implements each independently. Some sub-tests take half
|
|
9
|
+
minute to complete.
|
|
10
|
+
|
|
11
|
+
Failed instructions are listed as
|
|
12
|
+
|
|
13
|
+
[CB] opcode
|
|
14
|
+
|
|
15
|
+
Some errors cannot of course be diagnosed properly, since the test
|
|
16
|
+
framework itself relies on basic instruction behavior being correct.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
Internal operation
|
|
20
|
+
------------------
|
|
21
|
+
The main tests use a framework that runs each instruction in a loop,
|
|
22
|
+
varying the register values on input and examining them on output.
|
|
23
|
+
Rather than keep a table of correct values, it simply calculates a
|
|
24
|
+
CRC-32 checksum of all the output, then compares this with the correct
|
|
25
|
+
value. Instructions are divided into several groups, each with a
|
|
26
|
+
different set of input values suited for their behavior; for example,
|
|
27
|
+
the bit test instructions are fed $01, $02, $04 ... $40, $80, to ensure
|
|
28
|
+
each bit is handled properly, while the arithmetic instructions are fed
|
|
29
|
+
$01, $0F, $10, $7F, $FF, to exercise carry and half-carry. A few
|
|
30
|
+
instructions require a custom test due to their uniqueness.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Multi-ROM
|
|
34
|
+
---------
|
|
35
|
+
In the main directory is a single ROM which runs all the tests. It
|
|
36
|
+
prints a test's number, runs the test, then "ok" if it passes, otherwise
|
|
37
|
+
a failure code. Once all tests have completed it either reports that all
|
|
38
|
+
tests passed, or prints the number of failed tests. Finally, it makes
|
|
39
|
+
several beeps. If a test fails, it can be run on its own by finding the
|
|
40
|
+
corresponding ROM in individual/.
|
|
41
|
+
|
|
42
|
+
Ths compact format on screen is to avoid having the results scroll off
|
|
43
|
+
the top, so the test can be started and allowed to run without having to
|
|
44
|
+
constantly monitor the display.
|
|
45
|
+
|
|
46
|
+
Currently there is no well-defined way for an emulator test rig to
|
|
47
|
+
programatically find the result of the test; contact me if you're trying
|
|
48
|
+
to do completely automated testing of your emulator. One simple approach
|
|
49
|
+
is to take a screenshot after all tests have run, or even just a
|
|
50
|
+
checksum of one, and compare this with a previous run.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Failure codes
|
|
54
|
+
-------------
|
|
55
|
+
Failed tests may print a failure code, and also short description of the
|
|
56
|
+
problem. For more information about a failure code, look in the
|
|
57
|
+
corresponding source file in source/; the point in the code where
|
|
58
|
+
"set_test n" occurs is where that failure code will be generated.
|
|
59
|
+
Failure code 1 is a general failure of the test; any further information
|
|
60
|
+
will be printed.
|
|
61
|
+
|
|
62
|
+
Note that once a sub-test fails, no further tests for that file are run.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
Console output
|
|
66
|
+
--------------
|
|
67
|
+
Information is printed on screen in a way that needs only minimum LCD
|
|
68
|
+
support, and won't hang if LCD output isn't supported at all.
|
|
69
|
+
Specifically, while polling LY to wait for vblank, it will time out if
|
|
70
|
+
it takes too long, so LY always reading back as the same value won't
|
|
71
|
+
hang the test. It's also OK if scrolling isn't supported; in this case,
|
|
72
|
+
text will appear starting at the top of the screen.
|
|
73
|
+
|
|
74
|
+
Everything printed on screen is also sent to the game link port by
|
|
75
|
+
writing the character to SB, then writing $81 to SC. This is useful for
|
|
76
|
+
tests which print lots of information that scrolls off screen.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
Source code
|
|
80
|
+
-----------
|
|
81
|
+
Source code is included for all tests, in source/. It can be used to
|
|
82
|
+
build the individual test ROMs. Code for the multi test isn't included
|
|
83
|
+
due to the complexity of putting everything together.
|
|
84
|
+
|
|
85
|
+
Code is written for the wla-dx assembler. To assemble a particular test,
|
|
86
|
+
execute
|
|
87
|
+
|
|
88
|
+
wla -o "source_filename.s" test.o
|
|
89
|
+
wlalink linkfile test.gb
|
|
90
|
+
|
|
91
|
+
Test code uses a common shell framework contained in common/.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
Internal framework operation
|
|
95
|
+
----------------------------
|
|
96
|
+
Tests use a common framework for setting things up, reporting results,
|
|
97
|
+
and ending. All files first include "shell.inc", which sets up the ROM
|
|
98
|
+
header and shell code, and includes other commonly-used modules.
|
|
99
|
+
|
|
100
|
+
One oddity is that test code is first copied to internal RAM at $D000,
|
|
101
|
+
then executed there. This allows self-modification, and ensures the code
|
|
102
|
+
is executed the same way it is on my devcart, which doesn't have a
|
|
103
|
+
rewritable ROM as most do.
|
|
104
|
+
|
|
105
|
+
Some macros are used to simplify common tasks:
|
|
106
|
+
|
|
107
|
+
Macro Behavior
|
|
108
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
109
|
+
wreg addr,data Writes data to addr using LDH
|
|
110
|
+
lda addr Loads byte from addr into A using LDH
|
|
111
|
+
sta addr Stores A at addr using LDH
|
|
112
|
+
delay n Delays n cycles, where NOP = 1 cycle
|
|
113
|
+
delay_msec n Delays n milliseconds
|
|
114
|
+
set_test n,"Cause" Sets failure code and optional string
|
|
115
|
+
|
|
116
|
+
Routines and macros are documented where they are defined.
|
|
117
|
+
|
|
118
|
+
--
|
|
119
|
+
Shay Green <gblargg@gmail.com>
|
|
Binary file
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Game Boy CPU Instruction Timing Test
|
|
2
|
+
------------------------------------
|
|
3
|
+
This ROM tests the timings of all CPU instructions except HALT, STOP,
|
|
4
|
+
and the 11 illegal opcodes. For conditional instructions, it tests taken
|
|
5
|
+
and not taken timings. This test requires proper timer operation (TAC,
|
|
6
|
+
TIMA, TMA).
|
|
7
|
+
|
|
8
|
+
Failed instructions are listed as
|
|
9
|
+
|
|
10
|
+
[CB] opcode:measured time-correct time
|
|
11
|
+
|
|
12
|
+
Times are in terms of instruction cycles, where NOP takes one cycle.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Verified cycle timing tables
|
|
16
|
+
----------------------------
|
|
17
|
+
The test internally uses a table of proper cycle times, which can be
|
|
18
|
+
used in an emulator to ensure proper timing. The only changes below are
|
|
19
|
+
removal of the .byte prefixes, and addition of commas at the ends, so
|
|
20
|
+
that they can be used without changes in most programming languages. For
|
|
21
|
+
added correctness assurance, the original tables can be found at the end
|
|
22
|
+
of the source code.
|
|
23
|
+
|
|
24
|
+
Normal instructions:
|
|
25
|
+
|
|
26
|
+
1,3,2,2,1,1,2,1,5,2,2,2,1,1,2,1,
|
|
27
|
+
0,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1,
|
|
28
|
+
2,3,2,2,1,1,2,1,2,2,2,2,1,1,2,1,
|
|
29
|
+
2,3,2,2,3,3,3,1,2,2,2,2,1,1,2,1,
|
|
30
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
31
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
32
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
33
|
+
2,2,2,2,2,2,0,2,1,1,1,1,1,1,2,1,
|
|
34
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
35
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
36
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
37
|
+
1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
|
|
38
|
+
2,3,3,4,3,4,2,4,2,4,3,0,3,6,2,4,
|
|
39
|
+
2,3,3,0,3,4,2,4,2,4,3,0,3,0,2,4,
|
|
40
|
+
3,3,2,0,0,4,2,4,4,1,4,0,0,0,2,4,
|
|
41
|
+
3,3,2,1,0,4,2,4,3,2,4,1,0,0,2,4
|
|
42
|
+
|
|
43
|
+
CB-prefixed instructions:
|
|
44
|
+
|
|
45
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
46
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
47
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
48
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
49
|
+
2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
|
|
50
|
+
2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
|
|
51
|
+
2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
|
|
52
|
+
2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
|
|
53
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
54
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
55
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
56
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
57
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
58
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
59
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
|
|
60
|
+
2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
Internal operation
|
|
64
|
+
------------------
|
|
65
|
+
Before each instruction is executed, the test sets up registers and
|
|
66
|
+
memory in such a way that the instruction will cleanly execute and then
|
|
67
|
+
end up at a common destination, without trashing anything important. The
|
|
68
|
+
timing itself is done by first synchronizing to the timer via a loop,
|
|
69
|
+
executing the instruction, then using a similar loop to determine how
|
|
70
|
+
many clocks elapsed.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Failure codes
|
|
74
|
+
-------------
|
|
75
|
+
Failed tests may print a failure code, and also short description of the
|
|
76
|
+
problem. For more information about a failure code, look in the
|
|
77
|
+
corresponding source file in source/; the point in the code where
|
|
78
|
+
"set_test n" occurs is where that failure code will be generated.
|
|
79
|
+
Failure code 1 is a general failure of the test; any further information
|
|
80
|
+
will be printed.
|
|
81
|
+
|
|
82
|
+
Note that once a sub-test fails, no further tests for that file are run.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
Console output
|
|
86
|
+
--------------
|
|
87
|
+
Information is printed on screen in a way that needs only minimum LCD
|
|
88
|
+
support, and won't hang if LCD output isn't supported at all.
|
|
89
|
+
Specifically, while polling LY to wait for vblank, it will time out if
|
|
90
|
+
it takes too long, so LY always reading back as the same value won't
|
|
91
|
+
hang the test. It's also OK if scrolling isn't supported; in this case,
|
|
92
|
+
text will appear starting at the top of the screen.
|
|
93
|
+
|
|
94
|
+
Everything printed on screen is also sent to the game link port by
|
|
95
|
+
writing the character to SB, then writing $81 to SC. This is useful for
|
|
96
|
+
tests which print lots of information that scrolls off screen.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
Source code
|
|
100
|
+
-----------
|
|
101
|
+
Source code is included for all tests, in source/. It can be used to
|
|
102
|
+
build the individual test ROMs. Code for the multi test isn't included
|
|
103
|
+
due to the complexity of putting everything together.
|
|
104
|
+
|
|
105
|
+
Code is written for the wla-dx assembler. To assemble a particular test,
|
|
106
|
+
execute
|
|
107
|
+
|
|
108
|
+
wla -o "source_filename.s" test.o
|
|
109
|
+
wlalink linkfile test.gb
|
|
110
|
+
|
|
111
|
+
Test code uses a common shell framework contained in common/.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Internal framework operation
|
|
115
|
+
----------------------------
|
|
116
|
+
Tests use a common framework for setting things up, reporting results,
|
|
117
|
+
and ending. All files first include "shell.inc", which sets up the ROM
|
|
118
|
+
header and shell code, and includes other commonly-used modules.
|
|
119
|
+
|
|
120
|
+
One oddity is that test code is first copied to internal RAM at $D000,
|
|
121
|
+
then executed there. This allows self-modification, and ensures the code
|
|
122
|
+
is executed the same way it is on my devcart, which doesn't have a
|
|
123
|
+
rewritable ROM as most do.
|
|
124
|
+
|
|
125
|
+
Some macros are used to simplify common tasks:
|
|
126
|
+
|
|
127
|
+
Macro Behavior
|
|
128
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
129
|
+
wreg addr,data Writes data to addr using LDH
|
|
130
|
+
lda addr Loads byte from addr into A using LDH
|
|
131
|
+
sta addr Stores A at addr using LDH
|
|
132
|
+
delay n Delays n cycles, where NOP = 1 cycle
|
|
133
|
+
delay_msec n Delays n milliseconds
|
|
134
|
+
set_test n,"Cause" Sets failure code and optional string
|
|
135
|
+
|
|
136
|
+
Routines and macros are documented where they are defined.
|
|
137
|
+
|
|
138
|
+
--
|
|
139
|
+
Shay Green <gblargg@gmail.com>
|
data/lib/rubyboy/bus.rb
CHANGED
|
@@ -1,62 +1,201 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'cartridge/factory'
|
|
4
|
+
require_relative 'interrupt'
|
|
5
|
+
require_relative 'timer'
|
|
6
|
+
|
|
3
7
|
module Rubyboy
|
|
4
8
|
class Bus
|
|
5
|
-
attr_accessor :ppu, :rom
|
|
9
|
+
attr_accessor :ppu, :rom, :interrupt
|
|
6
10
|
|
|
7
|
-
def initialize(ppu, rom)
|
|
11
|
+
def initialize(ppu, rom, timer, interrupt)
|
|
8
12
|
@ppu = ppu
|
|
9
13
|
@rom = rom
|
|
14
|
+
@ram = Ram.new
|
|
15
|
+
@mbc = Cartridge::Factory.create(rom, @ram)
|
|
16
|
+
|
|
17
|
+
@interrupt = interrupt
|
|
18
|
+
@timer = timer
|
|
19
|
+
|
|
20
|
+
@oam = Array.new(0xa0, 0)
|
|
21
|
+
|
|
22
|
+
@tmp = {}
|
|
10
23
|
end
|
|
11
24
|
|
|
12
25
|
def read_byte(addr)
|
|
13
26
|
case addr
|
|
14
27
|
when 0x0000..0x7fff
|
|
15
|
-
@
|
|
28
|
+
@mbc.read_byte(addr)
|
|
16
29
|
when 0x8000..0x9fff
|
|
17
30
|
@ppu.vram[addr - 0x8000]
|
|
18
|
-
when
|
|
19
|
-
|
|
31
|
+
when 0xa000..0xdfff
|
|
32
|
+
@mbc.read_byte(addr)
|
|
33
|
+
when 0xe000..0xfdff
|
|
34
|
+
# echo ram
|
|
35
|
+
when 0xfe00..0xfe9f
|
|
36
|
+
@oam[addr - 0xfe00]
|
|
37
|
+
when 0xfea0..0xfeff
|
|
38
|
+
# unused
|
|
39
|
+
0xff
|
|
40
|
+
when 0xff00
|
|
41
|
+
# joypad
|
|
42
|
+
@tmp[addr] ||= 0
|
|
43
|
+
when 0xff01..0xff02
|
|
44
|
+
# serial
|
|
45
|
+
@tmp[addr] ||= 0
|
|
46
|
+
when 0xff04..0xff07
|
|
47
|
+
@timer.read_byte(addr)
|
|
48
|
+
when 0xff0f
|
|
49
|
+
@interrupt.if
|
|
50
|
+
when 0xff10..0xff26
|
|
51
|
+
# sound
|
|
52
|
+
@tmp[addr] ||= 0
|
|
53
|
+
when 0xff30..0xff3f
|
|
54
|
+
# wave pattern ram
|
|
55
|
+
@tmp[addr] ||= 0
|
|
20
56
|
when 0xff40
|
|
21
57
|
@ppu.lcdc
|
|
58
|
+
when 0xff41
|
|
59
|
+
# stat
|
|
60
|
+
@tmp[addr] ||= 0
|
|
22
61
|
when 0xff42
|
|
23
62
|
@ppu.scy
|
|
24
63
|
when 0xff43
|
|
25
64
|
@ppu.scx
|
|
26
65
|
when 0xff44
|
|
27
66
|
@ppu.ly
|
|
67
|
+
when 0xff45
|
|
68
|
+
@ppu.lyc
|
|
69
|
+
when 0xff46
|
|
70
|
+
# dma
|
|
71
|
+
@tmp[addr] ||= 0
|
|
28
72
|
when 0xff47
|
|
29
73
|
@ppu.bgp
|
|
74
|
+
when 0xff48
|
|
75
|
+
# obp0
|
|
76
|
+
@tmp[addr] ||= 0
|
|
77
|
+
when 0xff49
|
|
78
|
+
# obp1
|
|
79
|
+
@tmp[addr] ||= 0
|
|
80
|
+
when 0xff4a
|
|
81
|
+
# wy
|
|
82
|
+
@tmp[addr] ||= 0
|
|
83
|
+
when 0xff4b
|
|
84
|
+
# wx
|
|
85
|
+
@tmp[addr] ||= 0
|
|
86
|
+
when 0xff4f
|
|
87
|
+
# vbk
|
|
88
|
+
@tmp[addr] ||= 0
|
|
89
|
+
when 0xff50
|
|
90
|
+
# boot rom
|
|
91
|
+
@tmp[addr] ||= 0
|
|
92
|
+
when 0xff51..0xff55
|
|
93
|
+
# hdma
|
|
94
|
+
@tmp[addr] ||= 0
|
|
95
|
+
when 0xff68..0xff6b
|
|
96
|
+
# bgp
|
|
97
|
+
@tmp[addr] ||= 0
|
|
98
|
+
when 0xff70
|
|
99
|
+
# svbk
|
|
100
|
+
@tmp[addr] ||= 0
|
|
101
|
+
when 0xff80..0xfffe
|
|
102
|
+
@ram.hram[addr - 0xff80]
|
|
103
|
+
when 0xffff
|
|
104
|
+
@interrupt.ie
|
|
30
105
|
else
|
|
31
|
-
|
|
106
|
+
0xff
|
|
32
107
|
end
|
|
33
108
|
end
|
|
34
109
|
|
|
35
110
|
def write_byte(addr, value)
|
|
36
111
|
case addr
|
|
37
112
|
when 0x0000..0x7fff
|
|
38
|
-
@
|
|
113
|
+
@mbc.write_byte(addr, value)
|
|
39
114
|
when 0x8000..0x9fff
|
|
40
115
|
@ppu.vram[addr - 0x8000] = value
|
|
41
|
-
when
|
|
42
|
-
|
|
116
|
+
when 0xa000..0xdfff
|
|
117
|
+
@mbc.write_byte(addr, value)
|
|
118
|
+
when 0xe000..0xfdff
|
|
119
|
+
# echo ram
|
|
120
|
+
when 0xfe00..0xfe9f
|
|
121
|
+
@oam[addr - 0xfe00] = value
|
|
122
|
+
when 0xfea0..0xfeff
|
|
123
|
+
# unused
|
|
124
|
+
when 0xff00
|
|
125
|
+
# joypad
|
|
126
|
+
@tmp[addr] = value
|
|
127
|
+
when 0xff01..0xff02
|
|
128
|
+
# serial
|
|
129
|
+
@tmp[addr] = value
|
|
130
|
+
when 0xff04..0xff07
|
|
131
|
+
@timer.write_byte(addr, value)
|
|
132
|
+
when 0xff0f
|
|
133
|
+
@interrupt.if = value
|
|
134
|
+
when 0xff10..0xff26
|
|
135
|
+
# sound
|
|
136
|
+
@tmp[addr] = value
|
|
137
|
+
when 0xff30..0xff3f
|
|
138
|
+
# wave pattern ram
|
|
139
|
+
@tmp[addr] = value
|
|
43
140
|
when 0xff40
|
|
44
141
|
@ppu.lcdc = value
|
|
142
|
+
when 0xff41
|
|
143
|
+
# stat
|
|
144
|
+
@tmp[addr] = value
|
|
45
145
|
when 0xff42
|
|
46
146
|
@ppu.scy = value
|
|
47
147
|
when 0xff43
|
|
48
148
|
@ppu.scx = value
|
|
49
149
|
when 0xff44
|
|
50
150
|
@ppu.ly = value
|
|
151
|
+
when 0xff45
|
|
152
|
+
@ppu.lyc = value
|
|
153
|
+
when 0xff46
|
|
154
|
+
# dma
|
|
155
|
+
@tmp[addr] = value
|
|
51
156
|
when 0xff47
|
|
52
157
|
@ppu.bgp = value
|
|
53
|
-
|
|
54
|
-
|
|
158
|
+
when 0xff48
|
|
159
|
+
# obp0
|
|
160
|
+
@tmp[addr] = value
|
|
161
|
+
when 0xff49
|
|
162
|
+
# obp1
|
|
163
|
+
@tmp[addr] = value
|
|
164
|
+
when 0xff4a
|
|
165
|
+
# wy
|
|
166
|
+
@tmp[addr] = value
|
|
167
|
+
when 0xff4b
|
|
168
|
+
# wx
|
|
169
|
+
@tmp[addr] = value
|
|
170
|
+
when 0xff4f
|
|
171
|
+
# vbk
|
|
172
|
+
@tmp[addr] = value
|
|
173
|
+
when 0xff50
|
|
174
|
+
# boot rom
|
|
175
|
+
@tmp[addr] = value
|
|
176
|
+
when 0xff51..0xff55
|
|
177
|
+
# hdma
|
|
178
|
+
@tmp[addr] = value
|
|
179
|
+
when 0xff68..0xff6b
|
|
180
|
+
# bgp
|
|
181
|
+
@tmp[addr] = value
|
|
182
|
+
when 0xff70
|
|
183
|
+
# svbk
|
|
184
|
+
@tmp[addr] = value
|
|
185
|
+
when 0xff80..0xfffe
|
|
186
|
+
@ram.hram[addr - 0xff80] = value
|
|
187
|
+
when 0xffff
|
|
188
|
+
@interrupt.ie = value
|
|
55
189
|
end
|
|
56
190
|
end
|
|
57
191
|
|
|
58
192
|
def read_word(addr)
|
|
59
193
|
read_byte(addr) + (read_byte(addr + 1) << 8)
|
|
60
194
|
end
|
|
195
|
+
|
|
196
|
+
def write_word(addr, value)
|
|
197
|
+
write_byte(addr, value & 0xff)
|
|
198
|
+
write_byte(addr + 1, value >> 8)
|
|
199
|
+
end
|
|
61
200
|
end
|
|
62
201
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'nombc'
|
|
4
|
+
require_relative 'mbc1'
|
|
5
|
+
|
|
6
|
+
module Rubyboy
|
|
7
|
+
module Cartridge
|
|
8
|
+
class Factory
|
|
9
|
+
def self.create(rom, ram)
|
|
10
|
+
case rom.cartridge_type
|
|
11
|
+
when 0x00
|
|
12
|
+
Nombc.new(rom)
|
|
13
|
+
when 0x01..0x03
|
|
14
|
+
Mbc1.new(rom, ram)
|
|
15
|
+
else
|
|
16
|
+
raise "Unsupported cartridge type: #{cartridge_type}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../ram'
|
|
4
|
+
|
|
5
|
+
module Rubyboy
|
|
6
|
+
module Cartridge
|
|
7
|
+
class Mbc1
|
|
8
|
+
def initialize(rom, ram)
|
|
9
|
+
@rom = rom
|
|
10
|
+
@ram = ram
|
|
11
|
+
@rom_bank = 1
|
|
12
|
+
@ram_bank = 0
|
|
13
|
+
@ram_enable = false
|
|
14
|
+
@ram_banking_mode = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def read_byte(addr)
|
|
18
|
+
case addr
|
|
19
|
+
when 0x0000..0x3fff
|
|
20
|
+
@rom.data[addr]
|
|
21
|
+
when 0x4000..0x7fff
|
|
22
|
+
@rom.data[addr + (@rom_bank - 1) * 0x4000]
|
|
23
|
+
when 0xa000..0xbfff
|
|
24
|
+
if @ram_enable
|
|
25
|
+
if @ram_banking_mode
|
|
26
|
+
@ram.eram[addr - 0xa000 + @ram_bank * 0x800]
|
|
27
|
+
else
|
|
28
|
+
@ram.eram[addr - 0xa000]
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
0xff
|
|
32
|
+
end
|
|
33
|
+
when 0xc000..0xcfff
|
|
34
|
+
@ram.wram1[addr - 0xc000]
|
|
35
|
+
when 0xd000..0xdfff
|
|
36
|
+
@ram.wram2[addr - 0xd000]
|
|
37
|
+
else
|
|
38
|
+
raise "not implemented: read_byte #{addr}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def write_byte(addr, value)
|
|
43
|
+
case addr
|
|
44
|
+
when 0x0000..0x1fff
|
|
45
|
+
@ram_enable = value & 0x0f == 0x0a
|
|
46
|
+
@rom.data[addr] = value
|
|
47
|
+
when 0x2000..0x3fff
|
|
48
|
+
@rom_bank = value & 0x1f
|
|
49
|
+
@rom_bank = 1 if @rom_bank.zero?
|
|
50
|
+
@rom.data[addr] = value
|
|
51
|
+
when 0x4000..0x5fff
|
|
52
|
+
@ram_bank = value & 0x03
|
|
53
|
+
when 0x6000..0x7fff
|
|
54
|
+
@ram_banking_mode = value & 0x01 == 0x01
|
|
55
|
+
when 0xa000..0xbfff
|
|
56
|
+
if @ram_enable
|
|
57
|
+
if @ram_banking_mode
|
|
58
|
+
@ram.eram[addr - 0xa000 + @ram_bank * 0x800] = value
|
|
59
|
+
else
|
|
60
|
+
@ram.eram[addr - 0xa000] = value
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
when 0xc000..0xcfff
|
|
64
|
+
@ram.wram1[addr - 0xc000] = value
|
|
65
|
+
when 0xd000..0xdfff
|
|
66
|
+
@ram.wram2[addr - 0xd000] = value
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubyboy
|
|
4
|
+
module Cartridge
|
|
5
|
+
class Nombc
|
|
6
|
+
def initialize(rom)
|
|
7
|
+
@rom = rom
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def read_byte(addr)
|
|
11
|
+
@rom.data[addr]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def write_byte(_addr, _value)
|
|
15
|
+
# do nothing
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|