rubyboy 0.2.0 → 0.3.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 +3 -0
- data/CHANGELOG.md +10 -0
- data/lib/roms/bgbtest.gb +0 -0
- data/lib/roms/dmg-acid2.gb +0 -0
- data/lib/rubyboy/bus.rb +25 -69
- data/lib/rubyboy/cartridge/factory.rb +2 -0
- data/lib/rubyboy/cartridge/mbc1.rb +0 -10
- data/lib/rubyboy/cartridge/nombc.rb +6 -1
- data/lib/rubyboy/interrupt.rb +18 -0
- data/lib/rubyboy/lcd.rb +57 -0
- data/lib/rubyboy/ppu.rb +234 -41
- data/lib/rubyboy/version.rb +1 -1
- data/lib/rubyboy.rb +15 -24
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75741c0c0044a7a9985e38c81e7756ed155481f39a8ced6620842ad09590a631
|
4
|
+
data.tar.gz: 6ca44b4cf8b11111eaaae44876b1d303f7d9baf20ee6e2132b61c61bf5d93486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ef6237619ea02791de321e13645a694cdf715bdcd3f958d4116e739e4d6734309d7da66c03d829ffacec3e231310ea5d70ede2daf8c53b2bd93d7d1d483df19
|
7
|
+
data.tar.gz: ff8fd7354154f5f62b16ce5139a3c689d3e01f4e2dabe95e48350fe8573ddb490dc576a533c06d826974b20f3fcd7e245b5ada766cad2212a513d50ef632b59b
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2023-11-19
|
4
|
+
|
5
|
+
- Fix bg rendering
|
6
|
+
- Add render_window
|
7
|
+
- Add render_sprites
|
8
|
+
- Add interrupt in ppu
|
9
|
+
- Add oam_dma_transfer
|
10
|
+
- Use raylib for rendering
|
11
|
+
- Pass dmg-acid2 test
|
12
|
+
|
3
13
|
## [0.2.0] - 2023-11-14
|
4
14
|
|
5
15
|
- Add MBC1
|
data/lib/roms/bgbtest.gb
ADDED
Binary file
|
Binary file
|
data/lib/rubyboy/bus.rb
CHANGED
@@ -27,13 +27,17 @@ module Rubyboy
|
|
27
27
|
when 0x0000..0x7fff
|
28
28
|
@mbc.read_byte(addr)
|
29
29
|
when 0x8000..0x9fff
|
30
|
-
@ppu.
|
31
|
-
when 0xa000..
|
30
|
+
@ppu.read_byte(addr)
|
31
|
+
when 0xa000..0xbfff
|
32
32
|
@mbc.read_byte(addr)
|
33
|
+
when 0xc000..0xcfff
|
34
|
+
@ram.wram1[addr - 0xc000]
|
35
|
+
when 0xd000..0xdfff
|
36
|
+
@ram.wram2[addr - 0xd000]
|
33
37
|
when 0xe000..0xfdff
|
34
38
|
# echo ram
|
35
39
|
when 0xfe00..0xfe9f
|
36
|
-
@
|
40
|
+
@ppu.read_byte(addr)
|
37
41
|
when 0xfea0..0xfeff
|
38
42
|
# unused
|
39
43
|
0xff
|
@@ -46,43 +50,15 @@ module Rubyboy
|
|
46
50
|
when 0xff04..0xff07
|
47
51
|
@timer.read_byte(addr)
|
48
52
|
when 0xff0f
|
49
|
-
@interrupt.
|
53
|
+
@interrupt.read_byte(addr)
|
50
54
|
when 0xff10..0xff26
|
51
55
|
# sound
|
52
56
|
@tmp[addr] ||= 0
|
53
57
|
when 0xff30..0xff3f
|
54
58
|
# wave pattern ram
|
55
59
|
@tmp[addr] ||= 0
|
56
|
-
when 0xff40
|
57
|
-
@ppu.
|
58
|
-
when 0xff41
|
59
|
-
# stat
|
60
|
-
@tmp[addr] ||= 0
|
61
|
-
when 0xff42
|
62
|
-
@ppu.scy
|
63
|
-
when 0xff43
|
64
|
-
@ppu.scx
|
65
|
-
when 0xff44
|
66
|
-
@ppu.ly
|
67
|
-
when 0xff45
|
68
|
-
@ppu.lyc
|
69
|
-
when 0xff46
|
70
|
-
# dma
|
71
|
-
@tmp[addr] ||= 0
|
72
|
-
when 0xff47
|
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
|
60
|
+
when 0xff40..0xff4b
|
61
|
+
@ppu.read_byte(addr)
|
86
62
|
when 0xff4f
|
87
63
|
# vbk
|
88
64
|
@tmp[addr] ||= 0
|
@@ -101,7 +77,7 @@ module Rubyboy
|
|
101
77
|
when 0xff80..0xfffe
|
102
78
|
@ram.hram[addr - 0xff80]
|
103
79
|
when 0xffff
|
104
|
-
@interrupt.
|
80
|
+
@interrupt.read_byte(addr)
|
105
81
|
else
|
106
82
|
0xff
|
107
83
|
end
|
@@ -112,13 +88,17 @@ module Rubyboy
|
|
112
88
|
when 0x0000..0x7fff
|
113
89
|
@mbc.write_byte(addr, value)
|
114
90
|
when 0x8000..0x9fff
|
115
|
-
@ppu.
|
116
|
-
when 0xa000..
|
91
|
+
@ppu.write_byte(addr, value)
|
92
|
+
when 0xa000..0xbfff
|
117
93
|
@mbc.write_byte(addr, value)
|
94
|
+
when 0xc000..0xcfff
|
95
|
+
@ram.wram1[addr - 0xc000] = value
|
96
|
+
when 0xd000..0xdfff
|
97
|
+
@ram.wram2[addr - 0xd000] = value
|
118
98
|
when 0xe000..0xfdff
|
119
99
|
# echo ram
|
120
100
|
when 0xfe00..0xfe9f
|
121
|
-
@
|
101
|
+
@ppu.write_byte(addr, value)
|
122
102
|
when 0xfea0..0xfeff
|
123
103
|
# unused
|
124
104
|
when 0xff00
|
@@ -130,43 +110,19 @@ module Rubyboy
|
|
130
110
|
when 0xff04..0xff07
|
131
111
|
@timer.write_byte(addr, value)
|
132
112
|
when 0xff0f
|
133
|
-
@interrupt.
|
113
|
+
@interrupt.write_byte(addr, value)
|
134
114
|
when 0xff10..0xff26
|
135
115
|
# sound
|
136
116
|
@tmp[addr] = value
|
137
117
|
when 0xff30..0xff3f
|
138
118
|
# wave pattern ram
|
139
119
|
@tmp[addr] = value
|
140
|
-
when 0xff40
|
141
|
-
@ppu.lcdc = value
|
142
|
-
when 0xff41
|
143
|
-
# stat
|
144
|
-
@tmp[addr] = value
|
145
|
-
when 0xff42
|
146
|
-
@ppu.scy = value
|
147
|
-
when 0xff43
|
148
|
-
@ppu.scx = value
|
149
|
-
when 0xff44
|
150
|
-
@ppu.ly = value
|
151
|
-
when 0xff45
|
152
|
-
@ppu.lyc = value
|
153
120
|
when 0xff46
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
121
|
+
0xa0.times do |i|
|
122
|
+
write_byte(0xfe00 + i, read_byte((value << 8) + i))
|
123
|
+
end
|
124
|
+
when 0xff40..0xff4b
|
125
|
+
@ppu.write_byte(addr, value)
|
170
126
|
when 0xff4f
|
171
127
|
# vbk
|
172
128
|
@tmp[addr] = value
|
@@ -185,7 +141,7 @@ module Rubyboy
|
|
185
141
|
when 0xff80..0xfffe
|
186
142
|
@ram.hram[addr - 0xff80] = value
|
187
143
|
when 0xffff
|
188
|
-
@interrupt.
|
144
|
+
@interrupt.write_byte(addr, value)
|
189
145
|
end
|
190
146
|
end
|
191
147
|
|
@@ -30,10 +30,6 @@ module Rubyboy
|
|
30
30
|
else
|
31
31
|
0xff
|
32
32
|
end
|
33
|
-
when 0xc000..0xcfff
|
34
|
-
@ram.wram1[addr - 0xc000]
|
35
|
-
when 0xd000..0xdfff
|
36
|
-
@ram.wram2[addr - 0xd000]
|
37
33
|
else
|
38
34
|
raise "not implemented: read_byte #{addr}"
|
39
35
|
end
|
@@ -43,11 +39,9 @@ module Rubyboy
|
|
43
39
|
case addr
|
44
40
|
when 0x0000..0x1fff
|
45
41
|
@ram_enable = value & 0x0f == 0x0a
|
46
|
-
@rom.data[addr] = value
|
47
42
|
when 0x2000..0x3fff
|
48
43
|
@rom_bank = value & 0x1f
|
49
44
|
@rom_bank = 1 if @rom_bank.zero?
|
50
|
-
@rom.data[addr] = value
|
51
45
|
when 0x4000..0x5fff
|
52
46
|
@ram_bank = value & 0x03
|
53
47
|
when 0x6000..0x7fff
|
@@ -60,10 +54,6 @@ module Rubyboy
|
|
60
54
|
@ram.eram[addr - 0xa000] = value
|
61
55
|
end
|
62
56
|
end
|
63
|
-
when 0xc000..0xcfff
|
64
|
-
@ram.wram1[addr - 0xc000] = value
|
65
|
-
when 0xd000..0xdfff
|
66
|
-
@ram.wram2[addr - 0xd000] = value
|
67
57
|
end
|
68
58
|
end
|
69
59
|
end
|
data/lib/rubyboy/interrupt.rb
CHANGED
@@ -11,6 +11,24 @@ module Rubyboy
|
|
11
11
|
@if = 0
|
12
12
|
end
|
13
13
|
|
14
|
+
def read_byte(addr)
|
15
|
+
case addr
|
16
|
+
when 0xff0f
|
17
|
+
@if
|
18
|
+
when 0xffff
|
19
|
+
@ie
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def write_byte(addr, value)
|
24
|
+
case addr
|
25
|
+
when 0xff0f
|
26
|
+
@if = value
|
27
|
+
when 0xffff
|
28
|
+
@ie = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
14
32
|
def interrupts
|
15
33
|
@if & @ie & 0x1f
|
16
34
|
end
|
data/lib/rubyboy/lcd.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'raylib'
|
4
|
+
|
5
|
+
module Rubyboy
|
6
|
+
class Lcd
|
7
|
+
include Raylib
|
8
|
+
|
9
|
+
WIDTH = 160
|
10
|
+
HEIGHT = 144
|
11
|
+
SCALE = 4
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
load_raylib
|
15
|
+
InitWindow(WIDTH * SCALE, HEIGHT * SCALE, 'RUBY BOY')
|
16
|
+
image = GenImageColor(WIDTH, HEIGHT, BLACK)
|
17
|
+
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8
|
18
|
+
@texture = LoadTextureFromImage(image)
|
19
|
+
end
|
20
|
+
|
21
|
+
def draw(pixel_data)
|
22
|
+
UpdateTexture(@texture, pixel_data)
|
23
|
+
|
24
|
+
BeginDrawing()
|
25
|
+
ClearBackground(BLACK)
|
26
|
+
DrawTextureEx(@texture, Vector2.create(0, 0), 0.0, SCALE, WHITE)
|
27
|
+
EndDrawing()
|
28
|
+
end
|
29
|
+
|
30
|
+
def window_should_close?
|
31
|
+
WindowShouldClose()
|
32
|
+
end
|
33
|
+
|
34
|
+
def close_window
|
35
|
+
CloseWindow()
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def load_raylib
|
41
|
+
shared_lib_path = "#{Gem::Specification.find_by_name('raylib-bindings').full_gem_path}/lib/"
|
42
|
+
case RUBY_PLATFORM
|
43
|
+
when /mswin|msys|mingw/ # Windows
|
44
|
+
Raylib.load_lib("#{shared_lib_path}libraylib.dll")
|
45
|
+
when /darwin/ # macOS
|
46
|
+
Raylib.load_lib("#{shared_lib_path}libraylib.dylib")
|
47
|
+
when /linux/ # Ubuntu Linux (x86_64 or aarch64)
|
48
|
+
arch = RUBY_PLATFORM.split('-')[0]
|
49
|
+
Raylib.load_lib(shared_lib_path + "libraylib.#{arch}.so")
|
50
|
+
else
|
51
|
+
raise "Unknown system: #{RUBY_PLATFORM}"
|
52
|
+
end
|
53
|
+
|
54
|
+
SetTraceLogLevel(LOG_ERROR)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/rubyboy/ppu.rb
CHANGED
@@ -2,74 +2,267 @@
|
|
2
2
|
|
3
3
|
module Rubyboy
|
4
4
|
class Ppu
|
5
|
-
|
5
|
+
attr_reader :buffer
|
6
6
|
|
7
|
-
|
7
|
+
MODE = {
|
8
|
+
hblank: 0,
|
9
|
+
vblank: 1,
|
10
|
+
oam_scan: 2,
|
11
|
+
drawing: 3
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
LCD_WIDTH = 160
|
15
|
+
LCD_HEIGHT = 144
|
16
|
+
|
17
|
+
OAM_SCAN_CYCLES = 80
|
18
|
+
DRAWING_CYCLES = 172
|
19
|
+
HBLANK_CYCLES = 204
|
20
|
+
ONE_LINE_CYCLES = OAM_SCAN_CYCLES + DRAWING_CYCLES + HBLANK_CYCLES
|
21
|
+
|
22
|
+
def initialize(interrupt)
|
23
|
+
@mode = MODE[:oam_scan]
|
8
24
|
@lcdc = 0x91
|
25
|
+
@stat = 0x00
|
9
26
|
@scy = 0x00
|
10
27
|
@scx = 0x00
|
11
28
|
@ly = 0x00
|
12
29
|
@lyc = 0x00
|
13
|
-
@
|
14
|
-
@
|
30
|
+
@obp0 = 0x00
|
31
|
+
@obp1 = 0x00
|
32
|
+
@wy = 0x00
|
33
|
+
@wx = 0x00
|
34
|
+
@bgp = 0x00
|
35
|
+
@vram = Array.new(0x2000, 0x00)
|
36
|
+
@oam = Array.new(0xa0, 0x00)
|
37
|
+
@wly = 0x00
|
15
38
|
@cycles = 0
|
39
|
+
@interrupt = interrupt
|
40
|
+
@buffer = Array.new(144 * 160, 0x00)
|
41
|
+
end
|
42
|
+
|
43
|
+
def read_byte(addr)
|
44
|
+
case addr
|
45
|
+
when 0x8000..0x9fff
|
46
|
+
@mode == MODE[:drawing] ? 0xff : @vram[addr - 0x8000]
|
47
|
+
when 0xfe00..0xfe9f
|
48
|
+
@mode == MODE[:oam_scan] || @mode == MODE[:drawing] ? 0xff : @oam[addr - 0xfe00]
|
49
|
+
when 0xff40
|
50
|
+
@lcdc
|
51
|
+
when 0xff41
|
52
|
+
@stat | 0x80 | @mode
|
53
|
+
when 0xff42
|
54
|
+
@scy
|
55
|
+
when 0xff43
|
56
|
+
@scx
|
57
|
+
when 0xff44
|
58
|
+
@ly
|
59
|
+
when 0xff45
|
60
|
+
@lyc
|
61
|
+
when 0xff47
|
62
|
+
@bgp
|
63
|
+
when 0xff48
|
64
|
+
@obp0
|
65
|
+
when 0xff49
|
66
|
+
@obp1
|
67
|
+
when 0xff4a
|
68
|
+
@wy
|
69
|
+
when 0xff4b
|
70
|
+
@wx
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def write_byte(addr, value)
|
75
|
+
case addr
|
76
|
+
when 0x8000..0x9fff
|
77
|
+
@vram[addr - 0x8000] = value if @mode != MODE[:drawing]
|
78
|
+
when 0xfe00..0xfe9f
|
79
|
+
@oam[addr - 0xfe00] = value if @mode != MODE[:oam_scan] && @mode != MODE[:drawing]
|
80
|
+
when 0xff40
|
81
|
+
@lcdc = value
|
82
|
+
when 0xff41
|
83
|
+
@stat = value & 0x78
|
84
|
+
when 0xff42
|
85
|
+
@scy = value
|
86
|
+
when 0xff43
|
87
|
+
@scx = value
|
88
|
+
when 0xff44
|
89
|
+
# ly is read only
|
90
|
+
when 0xff45
|
91
|
+
@lyc = value
|
92
|
+
when 0xff46
|
93
|
+
# dma
|
94
|
+
when 0xff47
|
95
|
+
@bgp = value
|
96
|
+
when 0xff48
|
97
|
+
@obp0 = value
|
98
|
+
when 0xff49
|
99
|
+
@obp1 = value
|
100
|
+
when 0xff4a
|
101
|
+
@wy = value
|
102
|
+
when 0xff4b
|
103
|
+
@wx = value
|
104
|
+
end
|
16
105
|
end
|
17
106
|
|
18
107
|
def step(cycles)
|
108
|
+
return false if @lcdc[7].zero?
|
109
|
+
|
110
|
+
res = false
|
19
111
|
@cycles += cycles
|
20
|
-
return if @cycles < 456
|
21
112
|
|
22
|
-
@
|
23
|
-
|
113
|
+
case @mode
|
114
|
+
when MODE[:oam_scan]
|
115
|
+
if @cycles >= OAM_SCAN_CYCLES
|
116
|
+
@cycles -= OAM_SCAN_CYCLES
|
117
|
+
@mode = MODE[:drawing]
|
118
|
+
end
|
119
|
+
when MODE[:drawing]
|
120
|
+
if @cycles >= DRAWING_CYCLES
|
121
|
+
render_bg
|
122
|
+
render_window
|
123
|
+
render_sprites
|
124
|
+
@cycles -= DRAWING_CYCLES
|
125
|
+
@mode = MODE[:hblank]
|
126
|
+
@interrupt.request(0b0000_0010) if @stat[3] == 1
|
127
|
+
end
|
128
|
+
when MODE[:hblank]
|
129
|
+
if @cycles >= HBLANK_CYCLES
|
130
|
+
@cycles -= HBLANK_CYCLES
|
131
|
+
@ly += 1
|
132
|
+
handle_ly_eq_lyc
|
133
|
+
|
134
|
+
if @ly == LCD_HEIGHT
|
135
|
+
@mode = MODE[:vblank]
|
136
|
+
@interrupt.request(0b0000_0001)
|
137
|
+
@interrupt.request(0b0000_0010) if @stat[4] == 1
|
138
|
+
else
|
139
|
+
@mode = MODE[:oam_scan]
|
140
|
+
@interrupt.request(0b0000_0010) if @stat[5] == 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
when MODE[:vblank]
|
144
|
+
if @cycles >= ONE_LINE_CYCLES
|
145
|
+
@cycles -= ONE_LINE_CYCLES
|
146
|
+
@ly += 1
|
147
|
+
handle_ly_eq_lyc
|
148
|
+
|
149
|
+
if @ly == 154
|
150
|
+
@ly = 0
|
151
|
+
@wly = 0
|
152
|
+
handle_ly_eq_lyc
|
153
|
+
@mode = MODE[:oam_scan]
|
154
|
+
@interrupt.request(0b0000_0010) if @stat[5] == 1
|
155
|
+
res = true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
res
|
161
|
+
end
|
162
|
+
|
163
|
+
def render_bg
|
164
|
+
return if @lcdc[0].zero?
|
165
|
+
|
166
|
+
y = (@ly + @scy) % 256
|
167
|
+
LCD_WIDTH.times do |i|
|
168
|
+
x = (i + @scx) % 256
|
169
|
+
tile_index = get_tile_index(@lcdc[3], x, y)
|
170
|
+
pixel = get_pixel(tile_index, x, y)
|
171
|
+
@buffer[@ly * LCD_WIDTH + i] = get_color(@bgp, pixel)
|
172
|
+
end
|
24
173
|
end
|
25
174
|
|
26
|
-
def
|
27
|
-
|
28
|
-
tile_data_addr = @lcdc[4].zero? ? 0x9000 : 0x8000
|
175
|
+
def render_window
|
176
|
+
return if @lcdc[0].zero? || @lcdc[5].zero? || @ly < @wy
|
29
177
|
|
30
|
-
|
178
|
+
rendered = false
|
179
|
+
y = @wly
|
180
|
+
LCD_WIDTH.times do |i|
|
181
|
+
next if i < @wx - 7
|
31
182
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
183
|
+
rendered = true
|
184
|
+
x = i - (@wx - 7)
|
185
|
+
tile_index = get_tile_index(@lcdc[6], x, y)
|
186
|
+
pixel = get_pixel(tile_index, x, y)
|
187
|
+
@buffer[@ly * LCD_WIDTH + i] = get_color(@bgp, pixel)
|
188
|
+
end
|
189
|
+
@wly += 1 if rendered
|
190
|
+
end
|
37
191
|
|
38
|
-
|
192
|
+
def render_sprites
|
193
|
+
return if @lcdc[1].zero?
|
39
194
|
|
40
|
-
|
41
|
-
pixelsa = tile[i * 2]
|
42
|
-
pixelsb = tile[i * 2 + 1]
|
195
|
+
sprite_height = @lcdc[2].zero? ? 8 : 16
|
43
196
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
197
|
+
sprites = @oam.each_slice(4).filter_map do |sprite_attr|
|
198
|
+
sprite = {
|
199
|
+
y: (sprite_attr[0] - 16) % 256,
|
200
|
+
x: (sprite_attr[1] - 8) % 256,
|
201
|
+
tile_index: sprite_attr[2],
|
202
|
+
flags: sprite_attr[3]
|
203
|
+
}
|
204
|
+
next if sprite[:y] > @ly || sprite[:y] + sprite_height <= @ly
|
205
|
+
|
206
|
+
sprite
|
50
207
|
end
|
51
|
-
|
208
|
+
sprites = sprites.take(10).sort_by.with_index { |sprite, i| [-sprite[:x], -i] }
|
209
|
+
|
210
|
+
sprites.each do |sprite|
|
211
|
+
pallet = sprite[:flags][4].zero? ? @obp0 : @obp1
|
212
|
+
tile_index = sprite[:tile_index]
|
213
|
+
tile_index &= 0xfe if sprite_height == 16
|
214
|
+
y = (@ly - sprite[:y]) % 256
|
215
|
+
y = sprite_height - y - 1 if sprite[:flags][6] == 1
|
216
|
+
tile_index = (tile_index + 1) % 256 if y >= 8
|
217
|
+
y %= 8
|
218
|
+
|
219
|
+
8.times do |x|
|
220
|
+
x_flipped = sprite[:flags][5] == 1 ? 7 - x : x
|
221
|
+
|
222
|
+
pixel = get_pixel(tile_index, x_flipped, y)
|
223
|
+
i = (sprite[:x] + x) % 256
|
52
224
|
|
53
|
-
|
54
|
-
|
55
|
-
|
225
|
+
next if pixel.zero? || i >= LCD_WIDTH
|
226
|
+
next if sprite[:flags][7] == 1 && @buffer[@ly * LCD_WIDTH + i] != 0xff
|
227
|
+
|
228
|
+
@buffer[@ly * LCD_WIDTH + i] = get_color(pallet, pixel)
|
56
229
|
end
|
57
230
|
end
|
58
|
-
view_port
|
59
231
|
end
|
60
232
|
|
61
233
|
private
|
62
234
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
235
|
+
def get_tile_index(tile_map_area, x, y)
|
236
|
+
tile_map_addr = tile_map_area.zero? ? 0x1800 : 0x1c00
|
237
|
+
tile_map_index = (y / 8) * 32 + (x / 8)
|
238
|
+
tile_index = @vram[tile_map_addr + tile_map_index]
|
239
|
+
@lcdc[4].zero? ? to_signed_byte(tile_index) + 256 : tile_index
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_pixel(tile_index, x, y)
|
243
|
+
@vram[tile_index * 16 + (y % 8) * 2][7 - (x % 8)] + (@vram[tile_index * 16 + (y % 8) * 2 + 1][7 - (x % 8)] << 1)
|
244
|
+
end
|
245
|
+
|
246
|
+
def get_color(pallet, pixel)
|
247
|
+
case (pallet >> (pixel * 2)) & 0b11
|
248
|
+
when 0 then 0xff
|
249
|
+
when 1 then 0xaa
|
250
|
+
when 2 then 0x55
|
251
|
+
when 3 then 0x00
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def to_signed_byte(byte)
|
256
|
+
byte &= 0xff
|
257
|
+
byte > 127 ? byte - 256 : byte
|
258
|
+
end
|
259
|
+
|
260
|
+
def handle_ly_eq_lyc
|
261
|
+
if @ly == @lyc
|
262
|
+
@stat |= 0x04
|
263
|
+
@interrupt.request(0b0000_0010) if @stat[6] == 1
|
264
|
+
else
|
265
|
+
@stat &= 0xfb
|
73
266
|
end
|
74
267
|
end
|
75
268
|
end
|
data/lib/rubyboy/version.rb
CHANGED
data/lib/rubyboy.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'gosu'
|
4
3
|
require 'benchmark'
|
5
4
|
require_relative 'rubyboy/version'
|
6
5
|
require_relative 'rubyboy/bus'
|
@@ -8,52 +7,44 @@ require_relative 'rubyboy/cpu'
|
|
8
7
|
require_relative 'rubyboy/ppu'
|
9
8
|
require_relative 'rubyboy/rom'
|
10
9
|
require_relative 'rubyboy/timer'
|
10
|
+
require_relative 'rubyboy/lcd'
|
11
11
|
|
12
12
|
module Rubyboy
|
13
|
-
class Console
|
14
|
-
SCALE = 4
|
15
|
-
|
13
|
+
class Console
|
16
14
|
def initialize(rom_data)
|
17
|
-
super(160 * SCALE, 144 * SCALE, false)
|
18
|
-
# TODO: Display title
|
19
|
-
self.caption = 'RUBY BOY'
|
20
|
-
@total_cycles = 0
|
21
|
-
|
22
15
|
rom = Rom.new(rom_data)
|
23
16
|
interrupt = Interrupt.new
|
24
|
-
@ppu = Ppu.new
|
17
|
+
@ppu = Ppu.new(interrupt)
|
25
18
|
@timer = Timer.new(interrupt)
|
26
19
|
@bus = Bus.new(@ppu, rom, @timer, interrupt)
|
27
20
|
@cpu = Cpu.new(@bus)
|
21
|
+
@lcd = Lcd.new
|
28
22
|
end
|
29
23
|
|
30
|
-
def
|
31
|
-
|
24
|
+
def start
|
25
|
+
until @lcd.window_should_close?
|
32
26
|
cycles = @cpu.exec
|
33
|
-
@total_cycles += cycles
|
34
27
|
@timer.step(cycles)
|
35
|
-
@ppu.step(cycles)
|
28
|
+
draw if @ppu.step(cycles)
|
36
29
|
end
|
37
|
-
@
|
30
|
+
@lcd.close_window
|
38
31
|
rescue StandardError => e
|
39
32
|
p e.to_s[0, 100]
|
40
33
|
raise e
|
41
34
|
end
|
42
35
|
|
43
36
|
def draw
|
44
|
-
pixel_data = @ppu.
|
45
|
-
@
|
46
|
-
@image.draw(0, 0, 0, SCALE, SCALE)
|
37
|
+
pixel_data = buffer_to_pixel_data(@ppu.buffer)
|
38
|
+
@lcd.draw(pixel_data)
|
47
39
|
end
|
48
40
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
draw_quad(x, y, c, x + SCALE, y, c, x, y + SCALE, c, x + SCALE, y + SCALE, c)
|
41
|
+
def buffer_to_pixel_data(buffer)
|
42
|
+
buffer.map do |row|
|
43
|
+
[row, row, row]
|
44
|
+
end.flatten.pack('C*')
|
54
45
|
end
|
55
46
|
end
|
56
47
|
end
|
57
48
|
|
58
49
|
rom_data = File.open(File.expand_path('roms/cpu_instrs/cpu_instrs.gb', __dir__), 'r') { _1.read.bytes }
|
59
|
-
Rubyboy::Console.new(rom_data).
|
50
|
+
Rubyboy::Console.new(rom_data).start
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyboy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sacckey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: raylib-bindings
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.5.7
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.5.7
|
27
27
|
description:
|
28
28
|
email:
|
29
29
|
executables: []
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- README.md
|
39
39
|
- Rakefile
|
40
40
|
- lib/opcodes.json
|
41
|
+
- lib/roms/bgbtest.gb
|
41
42
|
- lib/roms/cpu_instrs/cpu_instrs.gb
|
42
43
|
- lib/roms/cpu_instrs/individual/01-special.gb
|
43
44
|
- lib/roms/cpu_instrs/individual/02-interrupts.gb
|
@@ -51,6 +52,7 @@ files:
|
|
51
52
|
- lib/roms/cpu_instrs/individual/10-bit ops.gb
|
52
53
|
- lib/roms/cpu_instrs/individual/11-op a,(hl).gb
|
53
54
|
- lib/roms/cpu_instrs/readme.txt
|
55
|
+
- lib/roms/dmg-acid2.gb
|
54
56
|
- lib/roms/hello-world.gb
|
55
57
|
- lib/roms/instr_timing/instr_timing.gb
|
56
58
|
- lib/roms/instr_timing/readme.txt
|
@@ -62,6 +64,7 @@ files:
|
|
62
64
|
- lib/rubyboy/cartridge/nombc.rb
|
63
65
|
- lib/rubyboy/cpu.rb
|
64
66
|
- lib/rubyboy/interrupt.rb
|
67
|
+
- lib/rubyboy/lcd.rb
|
65
68
|
- lib/rubyboy/ppu.rb
|
66
69
|
- lib/rubyboy/ram.rb
|
67
70
|
- lib/rubyboy/register.rb
|