GBRb 0.1.0 → 0.2.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/README.md +9 -1
  4. data/bin/gbrb +3 -3
  5. data/doc/images/blargg_cpu.png +0 -0
  6. data/doc/images/cpu_01.png +0 -0
  7. data/doc/images/cpu_03.png +0 -0
  8. data/doc/images/cpu_04.png +0 -0
  9. data/doc/images/cpu_05.png +0 -0
  10. data/doc/images/cpu_06.png +0 -0
  11. data/doc/images/cpu_07.png +0 -0
  12. data/doc/images/cpu_08.png +0 -0
  13. data/doc/images/cpu_09.png +0 -0
  14. data/doc/images/cpu_10.png +0 -0
  15. data/doc/images/cpu_11.png +0 -0
  16. data/doc/images/nintendo_logo.png +0 -0
  17. data/doc/images/opus5.png +0 -0
  18. data/doc/images/test.gb.png +0 -0
  19. data/doc/images/ttt.png +0 -0
  20. data/lib/gbrb.rb +7 -0
  21. data/lib/gbrb/cartridge.rb +21 -8
  22. data/lib/gbrb/cartridge/cartridge.rb +187 -0
  23. data/lib/gbrb/cpu/concatenated_register.rb +1 -1
  24. data/lib/gbrb/cpu/z80.rb +575 -498
  25. data/lib/gbrb/gb.rb +102 -32
  26. data/lib/gbrb/graphics.rb +1 -1
  27. data/lib/gbrb/graphics/gpu.rb +38 -30
  28. data/lib/gbrb/graphics/mode_clock.rb +31 -30
  29. data/lib/gbrb/graphics/screen_client.rb +3 -2
  30. data/lib/gbrb/instruction_set.rb +16 -0
  31. data/lib/gbrb/instruction_set/arithmetic.rb +238 -0
  32. data/lib/gbrb/instruction_set/bitwise.rb +64 -0
  33. data/lib/gbrb/instruction_set/boolean.rb +61 -0
  34. data/lib/gbrb/instruction_set/call.rb +40 -0
  35. data/lib/gbrb/instruction_set/carry.rb +23 -0
  36. data/lib/gbrb/instruction_set/cpl.rb +12 -0
  37. data/lib/gbrb/instruction_set/daa.rb +33 -0
  38. data/lib/gbrb/instruction_set/instruction.rb +23 -0
  39. data/lib/gbrb/instruction_set/jump.rb +47 -0
  40. data/lib/gbrb/instruction_set/ld.rb +241 -0
  41. data/lib/gbrb/instruction_set/return.rb +43 -0
  42. data/lib/gbrb/instruction_set/rotate.rb +178 -0
  43. data/lib/gbrb/instruction_set/rst.rb +16 -0
  44. data/lib/gbrb/instruction_set/special.rb +37 -0
  45. data/lib/gbrb/instruction_set/stack.rb +34 -0
  46. data/lib/gbrb/instruction_set/swap.rb +32 -0
  47. data/lib/gbrb/mmu.rb +60 -35
  48. data/lib/gbrb/options.rb +54 -0
  49. data/lib/gbrb/timer.rb +114 -0
  50. data/lib/gbrb/version.rb +1 -1
  51. data/misc/dump_diff +133 -0
  52. data/perf/cpu_perf_spec.rb +2 -2
  53. data/spec/gbrb/cartridge/cartridge_spec.rb +19 -0
  54. data/spec/gbrb/cartridge/mbc1_spec.rb +83 -0
  55. data/spec/gbrb/cpu/z80_spec.rb +92 -2
  56. data/spec/gbrb/{cpu/instruction_spec.rb → instruction_set/arithmetic_spec.rb} +21 -100
  57. data/spec/gbrb/instruction_set/boolean_spec.rb +50 -0
  58. data/spec/gbrb/instruction_set/instruction_spec.rb +22 -0
  59. data/spec/gbrb/instruction_set/ld_spec.rb +21 -0
  60. data/spec/gbrb/instruction_set/special_spec.rb +22 -0
  61. data/spec/gbrb/mmu_spec.rb +1 -1
  62. data/spec/gbrb/timer_spec.rb +26 -0
  63. metadata +53 -9
  64. data/lib/gbrb/cpu/instruction.rb +0 -648
  65. data/spec/gbrb/cartridge_spec.rb +0 -19
  66. data/spec/gbrb/graphics/mode_clock_spec.rb +0 -82
@@ -2,32 +2,46 @@ require_relative 'mmu'
2
2
  require_relative 'cpu/z80'
3
3
  require_relative 'graphics/gpu'
4
4
  require_relative 'cartridge'
5
-
6
- require 'thread'
5
+ require_relative 'version'
6
+ require_relative 'timer'
7
7
 
8
8
  module GBRb
9
9
  class GB
10
- def initialize cartridge_path='', debug=false
11
- @debug = debug
12
-
13
- @gpu_send = Queue.new
14
- @gpu_receive = Queue.new
10
+ def initialize config
11
+ @config = config
12
+ display_about_info
13
+ display_separator
15
14
 
16
- @mmu = GBRb::MMU.new read_bios, create_cartridge(cartridge_path), @gpu_send
15
+ bios = @config.skip_boot ? [] : read_bios
16
+ @mmu = GBRb::MMU.new bios
17
17
  @cpu = GBRb::CPU::Z80.new @mmu
18
- @gpu = GBRb::Graphics::GPU.new @mmu, @gpu_send, @gpu_receive
18
+ @gpu = GBRb::Graphics::GPU.new
19
+ @cartridge = GBRb::Cartridge.load_cartridge @config.cartridge_path
20
+ @timer = Timer.new
21
+
22
+ @mmu.connect @gpu, 0x8000..0x9fff
23
+ @mmu.connect @gpu, GBRb::Graphics::GPU::REGISTER_ADDRESSES
24
+ @mmu.connect @cartridge, 0x0000..0x7fff, 0xa000..0xbfff
25
+ @mmu.connect @timer, GBRb::Timer::ADDRESSES
26
+
27
+ @timer.irq_handler = @mmu
28
+
29
+ fake_boot_sequence if @config.skip_boot
19
30
  end
20
31
 
21
32
  def power_on
22
33
  @power = :on
23
34
  @gpu.power_on
24
- @mode = @debug ? :break : :default
35
+ @mode = @config.debug || :default
25
36
  @breakpoint = nil
26
37
 
27
- puts " pc | sp | a | b | c | d | e | h | l | f | CB | OP Code |" unless @mode == :default
38
+ unless @mode == :default
39
+ puts debug_help if @mode == :interactive
40
+ puts " pc | sp | a | b | c | d | e | h | l | f | CB | OP Code |"
41
+ end
28
42
 
29
43
  loop do
30
- if @mode == :break
44
+ if @mode == :interactive
31
45
  new_mode = nil
32
46
  until new_mode
33
47
  new_mode = debug_input
@@ -37,19 +51,24 @@ module GBRb
37
51
 
38
52
  pc = @cpu.r.pc.read
39
53
  op_code = @mmu.read_byte(pc).to_i
40
- prefix = @cpu.instance_eval '@cb_prefix'
41
54
 
42
55
  begin
43
- @gpu_send.push [:cpu, @cpu.step]
56
+ cycles = @cpu.step
57
+ @gpu.update! cycles
58
+ @timer.step cycles
44
59
  rescue GBRb::CPU::InstructionNotImplemented => e
45
- display_status prefix, op_code
60
+ display_status @cpu.instance_eval('@cb_prefix'), op_code
46
61
  @gpu.power_off
47
62
  exit
48
63
  end
49
64
 
50
- @gpu_receive.pop
51
- if @debug
52
- display_status prefix, op_code
65
+ prefix = @cpu.instance_eval '@cb_prefix'
66
+ op_code = @mmu.read_byte(pc + 1) if prefix
67
+
68
+ if @config.debug
69
+ if (@config.debug == :interactive) or (@config.debug == :dump and (op_code != 0xcb or prefix))
70
+ display_status prefix, op_code
71
+ end
53
72
  if @step
54
73
  @step -= 1
55
74
  if @step == 0
@@ -59,15 +78,69 @@ module GBRb
59
78
  end
60
79
  if @cpu.r.pc.read == @breakpoint
61
80
  @breakpoint = nil
62
- @mode = :break
81
+ @mode = :interactive
63
82
  end
64
83
  end
65
84
  end
66
85
  end
67
86
 
68
87
  private
88
+ def display_about_info
89
+ puts "GBRb. v#{GBRb::VERSION}"
90
+ puts "http://rampantmonkey.github.io/GBRb/"
91
+ end
92
+
93
+ def display_separator
94
+ puts '*'*80
95
+ end
96
+
97
+ def fake_boot_sequence
98
+ @cpu.r.pc.store 0x100
99
+ @cpu.r.a.store 0x01
100
+ @cpu.r.f.store 0xb0
101
+ @cpu.r.b.store 0x00
102
+ @cpu.r.c.store 0x13
103
+ @cpu.r.d.store 0x00
104
+ @cpu.r.e.store 0xd8
105
+ @cpu.r.h.store 0x01
106
+ @cpu.r.l.store 0x4d
107
+ @cpu.r.sp.store 0xfffe
108
+ @mmu.write_byte 0xff05, 0x00
109
+ @mmu.write_byte 0xff06, 0x00
110
+ @mmu.write_byte 0xff07, 0x00
111
+ @mmu.write_byte 0xff10, 0x80
112
+ @mmu.write_byte 0xff11, 0xbf
113
+ @mmu.write_byte 0xff12, 0xf3
114
+ @mmu.write_byte 0xff14, 0xbf
115
+ @mmu.write_byte 0xff16, 0x3f
116
+ @mmu.write_byte 0xff17, 0x00
117
+ @mmu.write_byte 0xff19, 0xbf
118
+ @mmu.write_byte 0xff1a, 0x7f
119
+ @mmu.write_byte 0xff1b, 0xff
120
+ @mmu.write_byte 0xff1c, 0x9f
121
+ @mmu.write_byte 0xff1e, 0xbf
122
+ @mmu.write_byte 0xff20, 0xff
123
+ @mmu.write_byte 0xff21, 0x00
124
+ @mmu.write_byte 0xff22, 0x00
125
+ @mmu.write_byte 0xff23, 0xbf
126
+ @mmu.write_byte 0xff24, 0x77
127
+ @mmu.write_byte 0xff25, 0xf3
128
+ @mmu.write_byte 0xff26, 0xf1
129
+ @mmu.write_byte 0xff40, 0x91
130
+ @mmu.write_byte 0xff42, 0x00
131
+ @mmu.write_byte 0xff43, 0x00
132
+ @mmu.write_byte 0xff45, 0x00
133
+ @mmu.write_byte 0xff47, 0xfc
134
+ @mmu.write_byte 0xff48, 0xff
135
+ @mmu.write_byte 0xff49, 0xff
136
+ @mmu.write_byte 0xff4a, 0x00
137
+ @mmu.write_byte 0xff4b, 0x00
138
+ @mmu.write_byte 0xff50, 0x00
139
+ @mmu.write_byte 0xffff, 0x00
140
+ end
141
+
69
142
  def debug_help
70
- "Available modes: (b)reakpoint (h)elp (q)uit (s)tep (t)iles"
143
+ "Available debugging modes: (b)reakpoint (g)pu (h)elp (m)emory (q)uit (s)tep (t)iles"
71
144
  end
72
145
 
73
146
  def debug_input
@@ -76,6 +149,13 @@ module GBRb
76
149
  when 'b'
77
150
  @breakpoint = input[1..-1].to_i 16
78
151
  :debug
152
+ when 'g'
153
+ command = input[1..-1].strip
154
+ begin
155
+ STDERR.puts @gpu.instance_eval(command)
156
+ rescue NameError
157
+ puts "#{command} is not a valid GPU operation"
158
+ end
79
159
  when 'h'
80
160
  STDERR.puts debug_help
81
161
  when 'm'
@@ -91,13 +171,13 @@ module GBRb
91
171
  when 's'
92
172
  @step = input[1..-1].to_i
93
173
  @step = 1 if @step == 0
94
- :debug
174
+ :step
95
175
  when 't'
96
176
  @gpu.dump_tiles
97
177
  nil
98
178
  else
99
179
  @step = 1
100
- :debug
180
+ :interactive
101
181
  end
102
182
  end
103
183
 
@@ -110,16 +190,6 @@ module GBRb
110
190
  def read_bios
111
191
  File.read(File.expand_path('../bios', __FILE__)).split.map{ |el| el.to_i(16) }
112
192
  end
113
-
114
- def create_cartridge path
115
- puts path
116
- f = File.open(path, 'r')
117
- c = Cartridge.new f
118
- f.close
119
- c
120
- rescue Errno::ENOENT
121
- raise InvalidCartridge, path
122
- end
123
193
  end
124
194
  end
125
195
 
@@ -4,7 +4,7 @@ module GBRb
4
4
  HEIGHT=144
5
5
 
6
6
  NUMBER_OF_COLORS = 4
7
- MAX_COLOR = NUMBER_OF_COLORS - 1
7
+ MAX_COLOR = 255
8
8
 
9
9
  SOCKET_PATH= '/tmp/gbrb.sock'
10
10
 
@@ -16,18 +16,21 @@ module GBRb::Graphics
16
16
  TILE_MAP_0 = 0x9800
17
17
  TILE_MAP_1 = 0x9c00
18
18
  TILE_SET_START = 0x8000
19
+ MEMORY_SIZE = 0x2000
19
20
 
20
- def initialize memory, incoming, outgoing
21
- @memory = memory
22
- @incoming = incoming
23
- @outgoing = outgoing
21
+ REGISTER_ADDRESSES = [CONTROL_REGISTER, SCROLL_Y_ADDRESS, SCROLL_X_ADDRESS, LINE_ADDRESS, PALETTE_REGISTER]
22
+
23
+ def initialize
24
24
  reset
25
25
  end
26
26
 
27
27
  def reset
28
+ @memory = Array.new MEMORY_SIZE, 0
29
+ @registers = REGISTER_ADDRESSES.inject({}){|acc, add| acc[add] = 0; acc}
28
30
  super
29
31
  create_frame_buffer
30
32
  create_tiles
33
+ update_palette
31
34
  end
32
35
 
33
36
  def create_tiles
@@ -41,12 +44,30 @@ module GBRb::Graphics
41
44
  end
42
45
  end
43
46
 
47
+ def read_byte address
48
+ if REGISTER_ADDRESSES.include? address
49
+ @registers[address]
50
+ else
51
+ @memory[address & (MEMORY_SIZE - 1)]
52
+ end
53
+ end
54
+
55
+ def write_byte address, value
56
+ if REGISTER_ADDRESSES.include? address
57
+ @registers[address] = value & 0xff
58
+ update_palette if address == PALETTE_REGISTER
59
+ else
60
+ @memory[address & (MEMORY_SIZE - 1)] = value & 0xff
61
+ update_tile address
62
+ end
63
+ end
64
+
44
65
  def update_tile address
45
66
  tile = ((address & 0x1ffe) >> 4) & 0x1ff
46
67
  y = ((address & 0x1ffe) >> 1) & 0x07
47
68
  8.times do |x|
48
69
  x_mask = 1 << (7-x)
49
- @tiles[tile][y][x] = ((@memory.read_byte(address) & x_mask) == x_mask ? 1 : 0) + ((@memory.read_byte(address+1) & x_mask) == x_mask ? 2 : 0)
70
+ @tiles[tile][y][x] = ((read_byte(address-1) & x_mask) == x_mask ? 1 : 0) + ((read_byte(address) & x_mask) == x_mask ? 2 : 0)
50
71
  end
51
72
  end
52
73
 
@@ -57,20 +78,7 @@ module GBRb::Graphics
57
78
  def power_on
58
79
  trap(:CHLD) { exit }
59
80
  @screen_pid = fork { start_screen }
60
-
61
- Thread.abort_on_exception = true
62
- Thread.new do
63
- loop do
64
- message = @incoming.pop
65
- case message.first
66
- when :cpu
67
- self.update! message.last.to_i if display_on?
68
- @outgoing.push :nothing
69
- when :mmu
70
- self.update_tile message.last.to_i
71
- end
72
- end
73
- end
81
+ screen.write @frame_buffer.map{|row| row.map{|pixel| @palette[pixel]}}
74
82
  end
75
83
 
76
84
  def power_off
@@ -93,7 +101,7 @@ module GBRb::Graphics
93
101
  row = []
94
102
  WIDTH.times do
95
103
  palette_index = @tiles[tile_index][tile_y][tile_x]
96
- color = palette[palette_index]
104
+ color = @palette[palette_index]
97
105
  row << color
98
106
  tile_x += 1
99
107
  if tile_x == 0x08
@@ -106,19 +114,19 @@ module GBRb::Graphics
106
114
  end
107
115
 
108
116
  def calculate_tile_number tilemap_offset, line_offset
109
- id = @memory.read_byte(tilemap_offset + line_offset)
117
+ id = read_byte(tilemap_offset + line_offset)
110
118
  id += 256 if background_tile_set == 0x10 and id < 128
111
119
  id
112
120
  end
113
121
 
114
- def palette
115
- value = @memory.read_byte PALETTE_REGISTER
122
+ def update_palette
123
+ value = read_byte PALETTE_REGISTER
116
124
  colors = []
117
125
  4.times do |i|
118
126
  mask = 0b11 << 2*i
119
- colors << (((value & mask) >> 2*i) ^ 0b11)
127
+ colors << ((value & mask) >> 2*i)
120
128
  end
121
- colors
129
+ @palette = colors
122
130
  end
123
131
 
124
132
  def dump_tiles
@@ -130,23 +138,23 @@ module GBRb::Graphics
130
138
  end
131
139
 
132
140
  def line
133
- @memory.read_byte LINE_ADDRESS
141
+ read_byte LINE_ADDRESS
134
142
  end
135
143
 
136
144
  def line= value
137
- @memory.write_byte LINE_ADDRESS, value
145
+ @registers[LINE_ADDRESS] = value & 0xff
138
146
  end
139
147
 
140
148
  def scroll_y
141
- @memory.read_byte SCROLL_Y_ADDRESS
149
+ read_byte SCROLL_Y_ADDRESS
142
150
  end
143
151
 
144
152
  def scroll_x
145
- @memory.read_byte SCROLL_X_ADDRESS
153
+ read_byte SCROLL_X_ADDRESS
146
154
  end
147
155
 
148
156
  def control_register
149
- @memory.read_byte CONTROL_REGISTER
157
+ read_byte CONTROL_REGISTER
150
158
  end
151
159
 
152
160
  def background_on?
@@ -6,44 +6,45 @@ module GBRb::Graphics
6
6
 
7
7
  def reset
8
8
  @clock = 0
9
- @mode = 2
9
+ @mode = 0
10
10
  self.line = 0
11
11
  end
12
12
 
13
13
  def update! time_increment
14
- @clock += time_increment
15
-
16
- case mode
17
- when 2
18
- if @clock >= 80
19
- @clock = 0
14
+ unless self.display_on?
15
+ self.line = 0
16
+ @clock = 456
17
+ @mode = 0
18
+ return
19
+ else
20
+ if line >= 144
21
+ @mode = 1
22
+ elsif @clock >= 456 - 80
23
+ @mode = 2
24
+ elsif @clock >= 456 - 80 - 172
20
25
  @mode = 3
21
- end
22
- when 3
23
- if @clock >= 172
24
- @clock = 0
26
+ else
25
27
  @mode = 0
26
- render_scan_line
27
28
  end
28
- when 0
29
- if @clock >= 204
30
- @clock = 0
31
- self.line = line.to_i + 1
32
- if line == 144
33
- @mode = 1
34
- draw_frame_buffer
35
- else
36
- @mode = 2
37
- end
29
+ end
30
+
31
+
32
+ @clock -= time_increment
33
+
34
+ if @clock <= 0
35
+ @clock += 456
36
+ self.line = line + 1
37
+
38
+ if line == 144
39
+ self.draw_frame_buffer
40
+ elsif line > 153
41
+ self.line = 0
38
42
  end
39
- when 1
40
- if @clock >= 456
41
- self.line = line + 1
42
- @clock = 0
43
- if line > 153
44
- self.line = 0
45
- @mode = 2
46
- end
43
+
44
+ if line < 144
45
+ if self.display_on?
46
+ render_scan_line
47
+ end
47
48
  end
48
49
  end
49
50
  end
@@ -3,11 +3,12 @@ require_relative '../graphics'
3
3
  require 'socket'
4
4
 
5
5
  module GBRb::Graphics
6
+ PPM_PALETTE = [0xeb, 0xc4, 0x60, 0x00]
6
7
  class ScreenClient
7
8
  def initialize
8
9
  trap(:PIPE) { exit }
9
10
  @connection = UNIXSocket.new SOCKET_PATH
10
- rescue Errno::ECONNREFUSED
11
+ rescue Errno::ECONNREFUSED, Errno::ENOENT
11
12
  sleep 0.1
12
13
  retry
13
14
  end
@@ -22,7 +23,7 @@ module GBRb::Graphics
22
23
  d = "P6 #{WIDTH} #{HEIGHT} #{MAX_COLOR} "
23
24
  data.each do |row|
24
25
  row.each do |pixel|
25
- d << pixel.chr * 3
26
+ d << PPM_PALETTE[pixel].chr * 3
26
27
  end
27
28
  end
28
29
  d << "\n"