rubyboy 1.3.1 → 1.3.2

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: d1065bd051087c47a5b6a73fdde2887b92a4eaceef15b73fb1ed0f0fc5d6ad2f
4
- data.tar.gz: 0e3f57c3c7258170c58c7ce855fa51352c989e1b07fbe3755d1c1bdf6134715d
3
+ metadata.gz: 4d92d323c7cf2886b1808eb6e8e2aabb786f5f4a4c66069e2788924ffa7ce72e
4
+ data.tar.gz: e0d5c4e1f1039f54283a5cd307c8fabe29f9dc019516501f9075560c16612af4
5
5
  SHA512:
6
- metadata.gz: 64e5b3e5fa9a54dee5f153a264816e9e1d307d7c95b349d9d92e717cb4f6725307e0d992c3294e4c9c06bee9f22c5ca04cc3257e756e453df432e8b02f244655
7
- data.tar.gz: 9b1ace44a5900036efabd776686cbf41bb68d8696176922fcbae445a0b2db84469f4383aae9299ddb3df12cbe70f89f703c6828c647a20b52628a0fdfd328a83
6
+ metadata.gz: 6960fa54f9150fafd79388eb9dafc9f787188a7ac1b097885ce3c8cbfe59ea7fbac84f3b8d3008f36c818ae3925b8965fbefaf8793d9211b37e8eb214dbbf9ca
7
+ data.tar.gz: 814ee8ce6c307828dde433847eabbb6d681c37d7bf424b6a0e90731c5bc3869060bc6baeaa79f15dfe5364532f070e02f97d4fb99844f115e8d341cc678ee0c4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.3.2] - 2024-05-04
4
+
5
+ - Revert "Enable YJIT when initialize"
6
+ - Add rubyboy-bench command
7
+ - Refactor Console class to use Emulator class
8
+ - Add option parsing and update default ROM path
9
+
3
10
  ## [1.3.1] - 2024-03-17
4
11
 
5
12
  - Enable YJIT when initialize
data/README.md CHANGED
@@ -6,6 +6,12 @@
6
6
 
7
7
  A Game Boy emulator written in Ruby
8
8
 
9
+ ## Screenshots
10
+ <div align="center">
11
+ <img src="/resource/screenshots/pokemon.png" width="400px"/>
12
+ <img src="/resource/screenshots/puyopuyo.png" width="400px"/>
13
+ </div>
14
+
9
15
  ## Requirements
10
16
  [SDL2](https://wiki.libsdl.org/SDL2/Installation)
11
17
 
@@ -26,7 +32,18 @@ If bundler is not being used to manage dependencies, install the gem by executin
26
32
 
27
33
  ## Usage
28
34
 
29
- $ rubyboy <rom_path>
35
+ $ RUBYOPT=--yjit rubyboy <rom_path>
36
+
37
+ | Key | Button |
38
+ | :---: | :----: |
39
+ | `W` | ↑ |
40
+ | `A` | ← |
41
+ | `S` | ↓ |
42
+ | `D` | → |
43
+ | `J` | A |
44
+ | `K` | B |
45
+ | `U` | Select |
46
+ | `I` | Start |
30
47
 
31
48
  ## Contributing
32
49
 
data/exe/rubyboy CHANGED
@@ -2,14 +2,30 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'rubyboy'
5
- require 'bench'
5
+ require 'optparse'
6
6
 
7
- arg = ARGV[0]
7
+ begin
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} ROM_PATH"
8
10
 
9
- if arg == 'bench'
10
- Rubyboy::Bench.new.bench
11
- elsif arg == 'stackprof'
12
- Rubyboy::Bench.new.stackprof
11
+ opts.on('-h', '--help', 'Displays this help') do
12
+ puts opts
13
+ exit
14
+ end
15
+ end.parse!
16
+ rescue OptionParser::ParseError => e
17
+ puts e.message
18
+ puts "Usage: #{File.basename($PROGRAM_NAME)} ROM_PATH"
19
+ exit 1
20
+ end
21
+
22
+ rom_path = ARGV.shift || 'lib/roms/tobu.gb'
23
+
24
+ puts "Ruby: #{RUBY_VERSION}"
25
+ if defined?(RubyVM::YJIT)
26
+ puts "YJIT: #{RubyVM::YJIT.enabled?}"
13
27
  else
14
- Rubyboy::Console.new(arg).start
28
+ puts 'YJIT is not available in this environment.'
15
29
  end
30
+
31
+ Rubyboy::Emulator.new(rom_path).start
data/exe/rubyboy-bench ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'rubyboy'
5
+ require 'bench'
6
+ require 'optparse'
7
+
8
+ options = {}
9
+ begin
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options]"
12
+
13
+ opts.on('--count COUNT', Integer, 'Number of counts for the benchmark') do |count|
14
+ options[:count] = count
15
+ end
16
+
17
+ opts.on('--frames FRAMES', Integer, 'Number of frames to process') do |frames|
18
+ options[:frames] = frames
19
+ end
20
+
21
+ opts.on('--rom-path ROM_PATH', String, 'Path to the ROM file') do |rom_path|
22
+ options[:rom_path] = rom_path
23
+ end
24
+
25
+ opts.on('-h', '--help', 'Displays this help') do
26
+ puts opts
27
+ exit
28
+ end
29
+ end.parse!
30
+ rescue OptionParser::ParseError => e
31
+ puts e.message
32
+ puts "Usage: #{File.basename($PROGRAM_NAME)} --count NUM --frames NUM --rom-path FILEPATH"
33
+ exit 1
34
+ end
35
+
36
+ puts "Ruby: #{RUBY_VERSION}"
37
+ if defined?(RubyVM::YJIT)
38
+ puts "YJIT: #{RubyVM::YJIT.enabled?}"
39
+ else
40
+ puts 'YJIT is not available in this environment.'
41
+ end
42
+
43
+ Rubyboy::Bench.new.bench(**options)
data/lib/bench.rb CHANGED
@@ -7,20 +7,19 @@ module Rubyboy
7
7
  class Bench
8
8
  def stackprof
9
9
  StackProf.run(mode: :cpu, out: 'stackprof-cpu-myapp.dump', raw: true) do
10
- Rubyboy::Console.new('lib/roms/tobu.gb').bench
10
+ Rubyboy::Emulator.new('lib/roms/tobu.gb').bench
11
11
  end
12
12
  end
13
13
 
14
- def bench
15
- bench_cnt = 3
14
+ def bench(count: 3, frames: 1500, rom_path: 'lib/roms/tobu.gb')
16
15
  time_sum = 0
17
- bench_cnt.times do |i|
18
- time = Rubyboy::Console.new('lib/roms/tobu.gb').bench
16
+ count.times do |i|
17
+ time = Rubyboy::Emulator.new(rom_path).bench(frames)
19
18
  time_sum += time
20
- puts "#{i + 1}: #{time} sec"
19
+ puts "#{i + 1}: #{time / 1_000_000_000.0} sec"
21
20
  end
22
21
 
23
- puts "FPS: #{1500 * bench_cnt / time_sum}"
22
+ puts "FPS: #{frames * count * 1_000_000_000.0 / time_sum}"
24
23
  end
25
24
  end
26
25
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubyboy
4
+ class Emulator
5
+ CPU_CLOCK_HZ = 4_194_304
6
+ CYCLE_NANOSEC = 1_000_000_000 / CPU_CLOCK_HZ
7
+
8
+ def initialize(rom_path)
9
+ rom_data = File.open(rom_path, 'r') { _1.read.bytes }
10
+ rom = Rom.new(rom_data)
11
+ ram = Ram.new
12
+ mbc = Cartridge::Factory.create(rom, ram)
13
+ interrupt = Interrupt.new
14
+ @ppu = Ppu.new(interrupt)
15
+ @timer = Timer.new(interrupt)
16
+ @joypad = Joypad.new(interrupt)
17
+ @apu = Apu.new
18
+ @bus = Bus.new(@ppu, rom, ram, mbc, @timer, interrupt, @joypad, @apu)
19
+ @cpu = Cpu.new(@bus, interrupt)
20
+ @lcd = Lcd.new
21
+ end
22
+
23
+ def start
24
+ SDL.InitSubSystem(SDL::INIT_KEYBOARD)
25
+
26
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
27
+ elapsed_machine_time = 0
28
+ catch(:exit_loop) do
29
+ loop do
30
+ elapsed_real_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - start_time
31
+ while elapsed_real_time > elapsed_machine_time
32
+ cycles = @cpu.exec
33
+ @timer.step(cycles)
34
+ @apu.step(cycles)
35
+ if @ppu.step(cycles)
36
+ @lcd.draw(@ppu.buffer)
37
+ key_input_check
38
+ throw :exit_loop if @lcd.window_should_close?
39
+ end
40
+
41
+ elapsed_machine_time += cycles * CYCLE_NANOSEC
42
+ end
43
+ end
44
+ end
45
+ @lcd.close_window
46
+ rescue StandardError => e
47
+ puts e.to_s[0, 100]
48
+ raise e
49
+ end
50
+
51
+ def bench(frames)
52
+ @lcd.close_window
53
+ frame_count = 0
54
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
55
+ while frame_count < frames
56
+ cycles = @cpu.exec
57
+ @timer.step(cycles)
58
+ if @ppu.step(cycles)
59
+ key_input_check
60
+ frame_count += 1
61
+ end
62
+ end
63
+
64
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - start_time
65
+ end
66
+
67
+ private
68
+
69
+ def key_input_check
70
+ SDL.PumpEvents
71
+ keyboard = SDL.GetKeyboardState(nil)
72
+ keyboard_state = keyboard.read_array_of_uint8(229)
73
+
74
+ direction = (keyboard_state[SDL::SDL_SCANCODE_D]) | (keyboard_state[SDL::SDL_SCANCODE_A] << 1) | (keyboard_state[SDL::SDL_SCANCODE_W] << 2) | (keyboard_state[SDL::SDL_SCANCODE_S] << 3)
75
+ action = (keyboard_state[SDL::SDL_SCANCODE_K]) | (keyboard_state[SDL::SDL_SCANCODE_J] << 1) | (keyboard_state[SDL::SDL_SCANCODE_U] << 2) | (keyboard_state[SDL::SDL_SCANCODE_I] << 3)
76
+ @joypad.direction_button(15 - direction)
77
+ @joypad.action_button(15 - action)
78
+ end
79
+ end
80
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubyboy
4
- VERSION = '1.3.1'
4
+ VERSION = '1.3.2'
5
5
  end
data/lib/rubyboy.rb CHANGED
@@ -4,6 +4,7 @@ require 'rubyboy/sdl'
4
4
  require_relative 'rubyboy/apu'
5
5
  require_relative 'rubyboy/bus'
6
6
  require_relative 'rubyboy/cpu'
7
+ require_relative 'rubyboy/emulator'
7
8
  require_relative 'rubyboy/ppu'
8
9
  require_relative 'rubyboy/rom'
9
10
  require_relative 'rubyboy/ram'
@@ -12,86 +13,3 @@ require_relative 'rubyboy/lcd'
12
13
  require_relative 'rubyboy/joypad'
13
14
  require_relative 'rubyboy/interrupt'
14
15
  require_relative 'rubyboy/cartridge/factory'
15
-
16
- module Rubyboy
17
- class Console
18
- CPU_CLOCK_HZ = 4_194_304
19
- CYCLE_NANOSEC = 1_000_000_000 / CPU_CLOCK_HZ
20
-
21
- def initialize(rom_path)
22
- rom_data = File.open(rom_path, 'r') { _1.read.bytes }
23
- rom = Rom.new(rom_data)
24
- ram = Ram.new
25
- mbc = Cartridge::Factory.create(rom, ram)
26
- interrupt = Interrupt.new
27
- @ppu = Ppu.new(interrupt)
28
- @timer = Timer.new(interrupt)
29
- @joypad = Joypad.new(interrupt)
30
- @apu = Apu.new
31
- @bus = Bus.new(@ppu, rom, ram, mbc, @timer, interrupt, @joypad, @apu)
32
- @cpu = Cpu.new(@bus, interrupt)
33
- @lcd = Lcd.new
34
-
35
- RubyVM::YJIT.enable
36
-
37
- puts "Ruby: #{RUBY_VERSION}"
38
- puts "YJIT: #{RubyVM::YJIT.enabled?}"
39
- end
40
-
41
- def start
42
- SDL.InitSubSystem(SDL::INIT_KEYBOARD)
43
-
44
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
45
- elapsed_machine_time = 0
46
- catch(:exit_loop) do
47
- loop do
48
- elapsed_real_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - start_time
49
- while elapsed_real_time > elapsed_machine_time
50
- cycles = @cpu.exec
51
- @timer.step(cycles)
52
- @apu.step(cycles)
53
- if @ppu.step(cycles)
54
- @lcd.draw(@ppu.buffer)
55
- key_input_check
56
- throw :exit_loop if @lcd.window_should_close?
57
- end
58
-
59
- elapsed_machine_time += cycles * CYCLE_NANOSEC
60
- end
61
- end
62
- end
63
- @lcd.close_window
64
- rescue StandardError => e
65
- puts e.to_s[0, 100]
66
- raise e
67
- end
68
-
69
- def bench
70
- cnt = 0
71
- start_time = Time.now
72
- while cnt < 1500
73
- cycles = @cpu.exec
74
- @timer.step(cycles)
75
- if @ppu.step(cycles)
76
- key_input_check
77
- cnt += 1
78
- end
79
- end
80
-
81
- Time.now - start_time
82
- end
83
-
84
- private
85
-
86
- def key_input_check
87
- SDL.PumpEvents
88
- keyboard = SDL.GetKeyboardState(nil)
89
- keyboard_state = keyboard.read_array_of_uint8(229)
90
-
91
- direction = (keyboard_state[SDL::SDL_SCANCODE_D]) | (keyboard_state[SDL::SDL_SCANCODE_A] << 1) | (keyboard_state[SDL::SDL_SCANCODE_W] << 2) | (keyboard_state[SDL::SDL_SCANCODE_S] << 3)
92
- action = (keyboard_state[SDL::SDL_SCANCODE_K]) | (keyboard_state[SDL::SDL_SCANCODE_J] << 1) | (keyboard_state[SDL::SDL_SCANCODE_U] << 2) | (keyboard_state[SDL::SDL_SCANCODE_I] << 3)
93
- @joypad.direction_button(15 - direction)
94
- @joypad.action_button(15 - action)
95
- end
96
- end
97
- end
Binary file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyboy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - sacckey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-17 00:00:00.000000000 Z
11
+ date: 2024-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -34,6 +34,7 @@ description:
34
34
  email:
35
35
  executables:
36
36
  - rubyboy
37
+ - rubyboy-bench
37
38
  extensions: []
38
39
  extra_rdoc_files: []
39
40
  files:
@@ -45,6 +46,7 @@ files:
45
46
  - README.md
46
47
  - Rakefile
47
48
  - exe/rubyboy
49
+ - exe/rubyboy-bench
48
50
  - lib/bench.rb
49
51
  - lib/opcodes.json
50
52
  - lib/roms/bgbtest.gb
@@ -78,6 +80,7 @@ files:
78
80
  - lib/rubyboy/cartridge/mbc1.rb
79
81
  - lib/rubyboy/cartridge/nombc.rb
80
82
  - lib/rubyboy/cpu.rb
83
+ - lib/rubyboy/emulator.rb
81
84
  - lib/rubyboy/interrupt.rb
82
85
  - lib/rubyboy/joypad.rb
83
86
  - lib/rubyboy/lcd.rb
@@ -93,6 +96,8 @@ files:
93
96
  - lib/rubyboy/version.rb
94
97
  - resource/logo/logo.png
95
98
  - resource/logo/logo.svg
99
+ - resource/screenshots/pokemon.png
100
+ - resource/screenshots/puyopuyo.png
96
101
  - sig/rubyboy.rbs
97
102
  homepage: https://github.com/sacckey/rubyboy
98
103
  licenses: