rubyboy 1.4.2 → 1.5.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/CHANGELOG.md +8 -1
- data/docs/worker.js +1 -1
- data/exe/rubyboy-bench +1 -1
- data/exe/rubyboy-wasm +1 -1
- data/lib/bench.rb +13 -11
- data/lib/rubyboy/bus.rb +78 -59
- data/lib/rubyboy/cartridge/mbc1.rb +34 -52
- data/lib/rubyboy/emulator_headless.rb +39 -0
- data/lib/rubyboy/version.rb +1 -1
- data/lib/stackprof.rb +14 -0
- metadata +5 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22c8e1f0094ec086f0e7130e6c79623d0e9385c718f0724ca2773c101ff3b976
|
4
|
+
data.tar.gz: 49adde27b593ef7bf8bf3f874160922ed7994388960db71f5757b2cbf033735f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48abbc79e8dde1e691f3f76a2736ad909b29af549c3c3b6a99eedd16a5b832f8b84bfb810ed4115483e116f5acbd50e1b07ec333e61778dc82433293f36a8401
|
7
|
+
data.tar.gz: dcf874037cd1d133687e3a99c5aeb36d97cd8a8f356514bb97b40eec57184b3bed7bd003538925fce18875d13d8bd37e1f50ffdd646568b5fbc351925f710a5a
|
data/CHANGELOG.md
CHANGED
data/docs/worker.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { DefaultRubyVM } from 'https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.7.
|
1
|
+
import { DefaultRubyVM } from 'https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.7.1/dist/browser/+esm';
|
2
2
|
import { File } from 'https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.3.0/+esm';
|
3
3
|
|
4
4
|
const DIRECTION_KEY_MASKS = {
|
data/exe/rubyboy-bench
CHANGED
data/exe/rubyboy-wasm
CHANGED
data/lib/bench.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require_relative 'rubyboy'
|
3
|
+
require_relative 'rubyboy/emulator_headless'
|
5
4
|
|
6
5
|
module Rubyboy
|
7
6
|
class Bench
|
8
|
-
def
|
9
|
-
StackProf.run(mode: :cpu, out: 'stackprof-cpu-myapp.dump', raw: true) do
|
10
|
-
Rubyboy::Emulator.new('lib/roms/tobu.gb').bench
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def bench(count: 3, frames: 1500, rom_path: 'lib/roms/tobu.gb')
|
7
|
+
def run(count: 3, frames: 1500, rom_path: 'lib/roms/tobu.gb')
|
15
8
|
time_sum = 0
|
9
|
+
|
16
10
|
count.times do |i|
|
17
|
-
|
18
|
-
|
11
|
+
emulator = Rubyboy::EmulatorHeadless.new(rom_path)
|
12
|
+
frame_count = 0
|
13
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
14
|
+
while frame_count < frames
|
15
|
+
emulator.step
|
16
|
+
frame_count += 1
|
17
|
+
end
|
18
|
+
time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - start_time
|
19
19
|
puts "#{i + 1}: #{time / 1_000_000_000.0} sec"
|
20
|
+
|
21
|
+
time_sum += time
|
20
22
|
end
|
21
23
|
|
22
24
|
puts "FPS: #{frames * count * 1_000_000_000.0 / time_sum}"
|
data/lib/rubyboy/bus.rb
CHANGED
@@ -11,74 +11,93 @@ module Rubyboy
|
|
11
11
|
@apu = apu
|
12
12
|
@interrupt = interrupt
|
13
13
|
@timer = timer
|
14
|
+
end
|
15
|
+
|
16
|
+
def read_byte(addr)
|
17
|
+
case addr >> 12
|
18
|
+
when 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb
|
19
|
+
return @mbc.read_byte(addr)
|
20
|
+
when 0x8, 0x9
|
21
|
+
return @ppu.read_byte(addr)
|
22
|
+
when 0xc
|
23
|
+
return @ram.wram1[addr - 0xc000]
|
24
|
+
when 0xd
|
25
|
+
return @ram.wram2[addr - 0xd000]
|
26
|
+
when 0xf
|
27
|
+
case addr >> 8
|
28
|
+
when 0xfe
|
29
|
+
return @ppu.read_byte(addr) if addr <= 0xfe9f
|
30
|
+
when 0xff
|
31
|
+
last_byte = addr & 0xFF
|
14
32
|
|
15
|
-
|
16
|
-
|
33
|
+
case last_byte
|
34
|
+
when 0x00
|
35
|
+
return @joypad.read_byte(addr)
|
36
|
+
when 0x04, 0x05, 0x06, 0x07
|
37
|
+
return @timer.read_byte(addr)
|
38
|
+
when 0x0f
|
39
|
+
return @interrupt.read_byte(addr)
|
40
|
+
when 0x46
|
41
|
+
return @ppu.read_byte(addr)
|
42
|
+
when 0xff
|
43
|
+
return @interrupt.read_byte(addr)
|
44
|
+
end
|
17
45
|
|
18
|
-
|
19
|
-
end
|
46
|
+
return @apu.read_byte(addr) if last_byte <= 0x26 && last_byte >= 0x10
|
20
47
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@
|
26
|
-
@write_methods[addr] = ->(value) { @mbc.write_byte(addr, value) }
|
27
|
-
when 0x8000..0x9fff
|
28
|
-
@read_methods[addr] = -> { @ppu.read_byte(addr) }
|
29
|
-
@write_methods[addr] = ->(value) { @ppu.write_byte(addr, value) }
|
30
|
-
when 0xa000..0xbfff
|
31
|
-
@read_methods[addr] = -> { @mbc.read_byte(addr) }
|
32
|
-
@write_methods[addr] = ->(value) { @mbc.write_byte(addr, value) }
|
33
|
-
when 0xc000..0xcfff
|
34
|
-
@read_methods[addr] = -> { @ram.wram1[addr - 0xc000] }
|
35
|
-
@write_methods[addr] = ->(value) { @ram.wram1[addr - 0xc000] = value }
|
36
|
-
when 0xd000..0xdfff
|
37
|
-
@read_methods[addr] = -> { @ram.wram2[addr - 0xd000] }
|
38
|
-
@write_methods[addr] = ->(value) { @ram.wram2[addr - 0xd000] = value }
|
39
|
-
when 0xfe00..0xfe9f
|
40
|
-
@read_methods[addr] = -> { @ppu.read_byte(addr) }
|
41
|
-
@write_methods[addr] = ->(value) { @ppu.write_byte(addr, value) }
|
42
|
-
when 0xff00
|
43
|
-
@read_methods[addr] = -> { @joypad.read_byte(addr) }
|
44
|
-
@write_methods[addr] = ->(value) { @joypad.write_byte(addr, value) }
|
45
|
-
when 0xff04..0xff07
|
46
|
-
@read_methods[addr] = -> { @timer.read_byte(addr) }
|
47
|
-
@write_methods[addr] = ->(value) { @timer.write_byte(addr, value) }
|
48
|
-
when 0xff0f
|
49
|
-
@read_methods[addr] = -> { @interrupt.read_byte(addr) }
|
50
|
-
@write_methods[addr] = ->(value) { @interrupt.write_byte(addr, value) }
|
51
|
-
when 0xff10..0xff26
|
52
|
-
@read_methods[addr] = -> { @apu.read_byte(addr) }
|
53
|
-
@write_methods[addr] = ->(value) { @apu.write_byte(addr, value) }
|
54
|
-
when 0xff30..0xff3f
|
55
|
-
@read_methods[addr] = -> { @apu.read_byte(addr) }
|
56
|
-
@write_methods[addr] = ->(value) { @apu.write_byte(addr, value) }
|
57
|
-
when 0xff46
|
58
|
-
@read_methods[addr] = -> { @ppu.read_byte(addr) }
|
59
|
-
@write_methods[addr] = ->(value) { 0xa0.times { |i| write_byte(0xfe00 + i, read_byte((value << 8) + i)) } }
|
60
|
-
when 0xff40..0xff4b
|
61
|
-
@read_methods[addr] = -> { @ppu.read_byte(addr) }
|
62
|
-
@write_methods[addr] = ->(value) { @ppu.write_byte(addr, value) }
|
63
|
-
when 0xff80..0xfffe
|
64
|
-
@read_methods[addr] = -> { @ram.hram[addr - 0xff80] }
|
65
|
-
@write_methods[addr] = ->(value) { @ram.hram[addr - 0xff80] = value }
|
66
|
-
when 0xffff
|
67
|
-
@read_methods[addr] = -> { @interrupt.read_byte(addr) }
|
68
|
-
@write_methods[addr] = ->(value) { @interrupt.write_byte(addr, value) }
|
69
|
-
else
|
70
|
-
@read_methods[addr] = -> { 0xff }
|
71
|
-
@write_methods[addr] = ->(_value) {}
|
48
|
+
return @apu.read_byte(addr) if last_byte <= 0x3f && last_byte >= 0x30
|
49
|
+
|
50
|
+
return @ppu.read_byte(addr) if last_byte <= 0x4b && last_byte >= 0x40
|
51
|
+
|
52
|
+
return @ram.hram[addr - 0xff80] if last_byte <= 0xfe && last_byte >= 0x80
|
72
53
|
end
|
73
54
|
end
|
74
|
-
end
|
75
55
|
|
76
|
-
|
77
|
-
@read_methods[addr].call
|
56
|
+
0xff
|
78
57
|
end
|
79
58
|
|
80
59
|
def write_byte(addr, value)
|
81
|
-
|
60
|
+
case addr >> 12
|
61
|
+
when 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb
|
62
|
+
return @mbc.write_byte(addr, value)
|
63
|
+
when 0x8, 0x9
|
64
|
+
return @ppu.write_byte(addr, value)
|
65
|
+
when 0xc
|
66
|
+
return @ram.wram1[addr - 0xc000] = value
|
67
|
+
when 0xd
|
68
|
+
return @ram.wram2[addr - 0xd000] = value
|
69
|
+
when 0xf
|
70
|
+
case addr >> 8
|
71
|
+
when 0xfe
|
72
|
+
return @ppu.write_byte(addr, value) if addr <= 0xfe9f
|
73
|
+
when 0xff
|
74
|
+
last_byte = addr & 0xFF
|
75
|
+
|
76
|
+
case last_byte
|
77
|
+
when 0x00
|
78
|
+
return @joypad.write_byte(addr, value)
|
79
|
+
when 0x04, 0x05, 0x06, 0x07
|
80
|
+
return @timer.write_byte(addr, value)
|
81
|
+
when 0x0f
|
82
|
+
return @interrupt.write_byte(addr, value)
|
83
|
+
when 0x46
|
84
|
+
0xa0.times { |i| write_byte(0xfe00 + i, read_byte((value << 8) + i)) }
|
85
|
+
return
|
86
|
+
when 0xff
|
87
|
+
return @interrupt.write_byte(addr, value)
|
88
|
+
end
|
89
|
+
|
90
|
+
return @apu.write_byte(addr, value) if last_byte <= 0x26 && last_byte >= 0x10
|
91
|
+
|
92
|
+
return @apu.write_byte(addr, value) if last_byte <= 0x3f && last_byte >= 0x30
|
93
|
+
|
94
|
+
return @ppu.write_byte(addr, value) if last_byte <= 0x4b && last_byte >= 0x40
|
95
|
+
|
96
|
+
return @ram.hram[addr - 0xff80] = value if last_byte <= 0xfe && last_byte >= 0x80
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
nil
|
82
101
|
end
|
83
102
|
|
84
103
|
def read_word(addr)
|
@@ -10,65 +10,47 @@ module Rubyboy
|
|
10
10
|
@ram_bank = 0
|
11
11
|
@ram_enable = false
|
12
12
|
@ram_banking_mode = false
|
13
|
-
|
14
|
-
@read_methods = Array.new(0xc000)
|
15
|
-
@write_methods = Array.new(0xc000)
|
16
|
-
|
17
|
-
set_methods
|
18
13
|
end
|
19
14
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@ram.eram[addr - 0xa000]
|
33
|
-
end
|
34
|
-
else
|
35
|
-
0xff
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
0xc000.times do |addr|
|
42
|
-
@write_methods[addr] =
|
43
|
-
case addr
|
44
|
-
when 0x0000..0x1fff then ->(value) { @ram_enable = value & 0x0f == 0x0a }
|
45
|
-
when 0x2000..0x3fff
|
46
|
-
lambda do |value|
|
47
|
-
@rom_bank = value & 0x1f
|
48
|
-
@rom_bank = 1 if @rom_bank == 0
|
49
|
-
end
|
50
|
-
when 0x4000..0x5fff then ->(value) { @ram_bank = value & 0x03 }
|
51
|
-
when 0x6000..0x7fff then ->(value) { @ram_banking_mode = value & 0x01 == 0x01 }
|
52
|
-
when 0xa000..0xbfff
|
53
|
-
lambda do |value|
|
54
|
-
if @ram_enable
|
55
|
-
if @ram_banking_mode
|
56
|
-
@ram.eram[addr - 0xa000 + @ram_bank * 0x800] = value
|
57
|
-
else
|
58
|
-
@ram.eram[addr - 0xa000] = value
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
15
|
+
def read_byte(addr)
|
16
|
+
case (addr >> 12)
|
17
|
+
when 0x0, 0x1, 0x2, 0x3
|
18
|
+
@rom.data[addr]
|
19
|
+
when 0x4, 0x5, 0x6, 0x7
|
20
|
+
@rom.data[addr + ((@rom_bank - 1) << 14)]
|
21
|
+
when 0xa, 0xb
|
22
|
+
if @ram_enable
|
23
|
+
if @ram_banking_mode
|
24
|
+
@ram.eram[addr - 0xa000 + (@ram_bank << 11)]
|
25
|
+
else
|
26
|
+
@ram.eram[addr - 0xa000]
|
62
27
|
end
|
28
|
+
else
|
29
|
+
0xff
|
30
|
+
end
|
63
31
|
end
|
64
32
|
end
|
65
33
|
|
66
|
-
def read_byte(addr)
|
67
|
-
@read_methods[addr].call
|
68
|
-
end
|
69
|
-
|
70
34
|
def write_byte(addr, value)
|
71
|
-
|
35
|
+
case addr >> 12
|
36
|
+
when 0x0, 0x1
|
37
|
+
@ram_enable = value & 0x0f == 0x0a
|
38
|
+
when 0x2, 0x3
|
39
|
+
@rom_bank = value & 0x1f
|
40
|
+
@rom_bank = 1 if @rom_bank == 0
|
41
|
+
when 0x4, 0x5
|
42
|
+
@ram_bank = value & 0x03
|
43
|
+
when 0x6, 0x7
|
44
|
+
@ram_banking_mode = value & 0x01 == 0x01
|
45
|
+
when 0xa, 0xb
|
46
|
+
if @ram_enable
|
47
|
+
if @ram_banking_mode
|
48
|
+
@ram.eram[addr - 0xa000 + (@ram_bank << 11)] = value
|
49
|
+
else
|
50
|
+
@ram.eram[addr - 0xa000] = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
72
54
|
end
|
73
55
|
end
|
74
56
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'apu'
|
4
|
+
require_relative 'bus'
|
5
|
+
require_relative 'cpu'
|
6
|
+
require_relative 'ppu'
|
7
|
+
require_relative 'rom'
|
8
|
+
require_relative 'ram'
|
9
|
+
require_relative 'timer'
|
10
|
+
require_relative 'joypad'
|
11
|
+
require_relative 'interrupt'
|
12
|
+
require_relative 'cartridge/factory'
|
13
|
+
|
14
|
+
module Rubyboy
|
15
|
+
class EmulatorHeadless
|
16
|
+
def initialize(rom_path)
|
17
|
+
rom_data = File.open(rom_path, 'r') { _1.read.bytes }
|
18
|
+
rom = Rom.new(rom_data)
|
19
|
+
ram = Ram.new
|
20
|
+
mbc = Cartridge::Factory.create(rom, ram)
|
21
|
+
interrupt = Interrupt.new
|
22
|
+
@ppu = Ppu.new(interrupt)
|
23
|
+
@timer = Timer.new(interrupt)
|
24
|
+
joypad = Joypad.new(interrupt)
|
25
|
+
@apu = Apu.new
|
26
|
+
bus = Bus.new(@ppu, rom, ram, mbc, @timer, interrupt, joypad, @apu)
|
27
|
+
@cpu = Cpu.new(bus, interrupt)
|
28
|
+
end
|
29
|
+
|
30
|
+
def step
|
31
|
+
loop do
|
32
|
+
cycles = @cpu.exec
|
33
|
+
@timer.step(cycles)
|
34
|
+
@apu.step(cycles)
|
35
|
+
break if @ppu.step(cycles)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/rubyboy/version.rb
CHANGED
data/lib/stackprof.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stackprof'
|
4
|
+
require_relative 'rubyboy/emulator'
|
5
|
+
|
6
|
+
module Rubyboy
|
7
|
+
class Stackprof
|
8
|
+
def run
|
9
|
+
StackProf.run(mode: :cpu, out: 'stackprof-cpu-myapp.dump', raw: true) do
|
10
|
+
Rubyboy::Emulator.new('lib/roms/tobu.gb').bench
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyboy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sacckey
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-02-16 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: ffi
|
@@ -30,8 +29,6 @@ dependencies:
|
|
30
29
|
- - ">="
|
31
30
|
- !ruby/object:Gem::Version
|
32
31
|
version: 1.16.3
|
33
|
-
description:
|
34
|
-
email:
|
35
32
|
executables:
|
36
33
|
- rubyboy
|
37
34
|
- rubyboy-bench
|
@@ -91,6 +88,7 @@ files:
|
|
91
88
|
- lib/rubyboy/cartridge/nombc.rb
|
92
89
|
- lib/rubyboy/cpu.rb
|
93
90
|
- lib/rubyboy/emulator.rb
|
91
|
+
- lib/rubyboy/emulator_headless.rb
|
94
92
|
- lib/rubyboy/emulator_wasm.rb
|
95
93
|
- lib/rubyboy/interrupt.rb
|
96
94
|
- lib/rubyboy/joypad.rb
|
@@ -105,6 +103,7 @@ files:
|
|
105
103
|
- lib/rubyboy/sdl.rb
|
106
104
|
- lib/rubyboy/timer.rb
|
107
105
|
- lib/rubyboy/version.rb
|
106
|
+
- lib/stackprof.rb
|
108
107
|
- resource/logo/logo.png
|
109
108
|
- resource/logo/logo.svg
|
110
109
|
- resource/logo/rubyboy.png
|
@@ -120,7 +119,6 @@ metadata:
|
|
120
119
|
source_code_uri: https://github.com/sacckey/rubyboy
|
121
120
|
changelog_uri: https://github.com/sacckey/rubyboy/blob/main/CHANGELOG.md
|
122
121
|
rubygems_mfa_required: 'true'
|
123
|
-
post_install_message:
|
124
122
|
rdoc_options: []
|
125
123
|
require_paths:
|
126
124
|
- lib
|
@@ -135,8 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
133
|
- !ruby/object:Gem::Version
|
136
134
|
version: '0'
|
137
135
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
139
|
-
signing_key:
|
136
|
+
rubygems_version: 3.6.2
|
140
137
|
specification_version: 4
|
141
138
|
summary: A Game Boy emulator written in Ruby
|
142
139
|
test_files: []
|