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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c4b114437f8fdfe135e5dbb47c8ce18de914bf3bff25376a28d54a4550d3bfc
4
- data.tar.gz: f6f8714c97f42513c68d2a371742490a3a7ed295e056965e2bf47a50495fad0f
3
+ metadata.gz: 75741c0c0044a7a9985e38c81e7756ed155481f39a8ced6620842ad09590a631
4
+ data.tar.gz: 6ca44b4cf8b11111eaaae44876b1d303f7d9baf20ee6e2132b61c61bf5d93486
5
5
  SHA512:
6
- metadata.gz: 810c319bb3b7766b277ba715c82d959bf97e5f16ad35994f3a84b7bd2726f24adac056427e067313b73c2f093c957d1a8f060b41b979083a34ceca5a9b20d956
7
- data.tar.gz: 70b411a71e7604f00b3f16257d8ccceec77795d0156e79f04c85b95093853a755fa75d949045f752c325f095e3427049fd71127436e50f77aa3f90be401009a2
6
+ metadata.gz: 0ef6237619ea02791de321e13645a694cdf715bdcd3f958d4116e739e4d6734309d7da66c03d829ffacec3e231310ea5d70ede2daf8c53b2bd93d7d1d483df19
7
+ data.tar.gz: ff8fd7354154f5f62b16ce5139a3c689d3e01f4e2dabe95e48350fe8573ddb490dc576a533c06d826974b20f3fcd7e245b5ada766cad2212a513d50ef632b59b
data/.rubocop.yml CHANGED
@@ -14,6 +14,9 @@ Layout/LineLength:
14
14
  Metrics/AbcSize:
15
15
  Enabled: false
16
16
 
17
+ Metrics/BlockNesting:
18
+ Enabled: false
19
+
17
20
  Metrics/CyclomaticComplexity:
18
21
  Enabled: false
19
22
 
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
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.vram[addr - 0x8000]
31
- when 0xa000..0xdfff
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
- @oam[addr - 0xfe00]
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.if
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.lcdc
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.ie
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.vram[addr - 0x8000] = value
116
- when 0xa000..0xdfff
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
- @oam[addr - 0xfe00] = value
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.if = value
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
- # dma
155
- @tmp[addr] = value
156
- when 0xff47
157
- @ppu.bgp = value
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
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.ie = value
144
+ @interrupt.write_byte(addr, value)
189
145
  end
190
146
  end
191
147
 
@@ -12,6 +12,8 @@ module Rubyboy
12
12
  Nombc.new(rom)
13
13
  when 0x01..0x03
14
14
  Mbc1.new(rom, ram)
15
+ when 0x08..0x09
16
+ Nombc.new(rom)
15
17
  else
16
18
  raise "Unsupported cartridge type: #{cartridge_type}"
17
19
  end
@@ -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
@@ -8,7 +8,12 @@ module Rubyboy
8
8
  end
9
9
 
10
10
  def read_byte(addr)
11
- @rom.data[addr]
11
+ case addr
12
+ when 0x0000..0x7fff
13
+ @rom.data[addr]
14
+ else
15
+ raise "not implemented: read_byte #{addr}"
16
+ end
12
17
  end
13
18
 
14
19
  def write_byte(_addr, _value)
@@ -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
@@ -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
- attr_accessor :lcdc, :scy, :scx, :ly, :lyc, :bgp, :vram, :cycles
5
+ attr_reader :buffer
6
6
 
7
- def initialize
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
- @bgp = 0xfc
14
- @vram = Array.new(8192, 0x00)
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
- @cycles -= 456
23
- @ly = (@ly + 1) % 154
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 draw_bg
27
- tile_map_addr = @lcdc[3].zero? ? 0x9800 : 0x9c00
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
- bg = Array.new(256) { Array.new(256, 0x00) }
178
+ rendered = false
179
+ y = @wly
180
+ LCD_WIDTH.times do |i|
181
+ next if i < @wx - 7
31
182
 
32
- 32.times do |y|
33
- 32.times do |x|
34
- tile_map_index = (y * 32 + x)
35
- tile_index = @vram[tile_map_addr - 0x8000 + tile_map_index]
36
- # TODO: if @lcdc[4] == 0 then the addr becomes signed
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
- tile = @vram[tile_data_addr - 0x8000 + tile_index * 16, 16]
192
+ def render_sprites
193
+ return if @lcdc[1].zero?
39
194
 
40
- 8.times do |i|
41
- pixelsa = tile[i * 2]
42
- pixelsb = tile[i * 2 + 1]
195
+ sprite_height = @lcdc[2].zero? ? 8 : 16
43
196
 
44
- 8.times do |j|
45
- c = (pixelsb[7 - j] << 1) + pixelsa[7 - j]
46
- bg[y * 8 + i][x * 8 + j] = get_color(c)
47
- end
48
- end
49
- end
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
- view_port = Array.new(144) { Array.new(160, 0x00) }
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
- 144.times do |y|
54
- 160.times do |x|
55
- view_port[y][x] = bg[(y + @scy) % 144][(x + @scx) % 160]
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 get_color(color_num)
64
- case color_num
65
- when 0
66
- [0xff, 0xff, 0xff, 0xff]
67
- when 1
68
- [0xcc, 0xcc, 0xcc, 0xff]
69
- when 2
70
- [0x77, 0x77, 0x77, 0xff]
71
- when 3
72
- [0x00, 0x00, 0x00, 0xff]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubyboy
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
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 < Gosu::Window
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 update
31
- while @total_cycles < 70224
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
- @total_cycles -= 70224
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.draw_bg.flatten.pack('C*')
45
- @image = Gosu::Image.from_blob(160, 144, pixel_data)
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 draw_pixel(x, y, color)
50
- x *= SCALE
51
- y *= SCALE
52
- c = Gosu::Color.new(255, color[0], color[1], color[2])
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).show
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.2.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-14 00:00:00.000000000 Z
11
+ date: 2023-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: gosu
14
+ name: raylib-bindings
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.4'
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: '1.4'
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