rubyboy 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
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: