teek 0.1.2 → 0.1.4
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/README.md +21 -0
- data/Rakefile +121 -22
- data/ext/teek/extconf.rb +19 -1
- data/ext/teek/tcltkbridge.c +44 -2
- data/ext/teek/tcltkbridge.h +3 -0
- data/ext/teek/tkdrop.c +66 -0
- data/ext/teek/tkdrop.h +26 -0
- data/ext/teek/tkdrop_macos.m +141 -0
- data/ext/teek/tkdrop_win.c +232 -0
- data/ext/teek/tkdrop_x11.c +337 -0
- data/ext/teek/tkwin.c +42 -0
- data/lib/teek/platform.rb +29 -0
- data/lib/teek/version.rb +1 -1
- data/lib/teek.rb +248 -7
- data/teek.gemspec +3 -2
- metadata +7 -45
- data/sample/calculator.rb +0 -255
- data/sample/debug_demo.rb +0 -43
- data/sample/goldberg.rb +0 -1803
- data/sample/goldberg_helpers.rb +0 -170
- data/sample/minesweeper/assets/MINESWEEPER_0.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_1.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_2.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_3.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_4.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_5.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_6.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_7.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_8.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_F.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_M.png +0 -0
- data/sample/minesweeper/assets/MINESWEEPER_X.png +0 -0
- data/sample/minesweeper/minesweeper.rb +0 -452
- data/sample/optcarrot/vendor/optcarrot/apu.rb +0 -856
- data/sample/optcarrot/vendor/optcarrot/config.rb +0 -257
- data/sample/optcarrot/vendor/optcarrot/cpu.rb +0 -1162
- data/sample/optcarrot/vendor/optcarrot/driver.rb +0 -144
- data/sample/optcarrot/vendor/optcarrot/mapper/cnrom.rb +0 -14
- data/sample/optcarrot/vendor/optcarrot/mapper/mmc1.rb +0 -105
- data/sample/optcarrot/vendor/optcarrot/mapper/mmc3.rb +0 -153
- data/sample/optcarrot/vendor/optcarrot/mapper/uxrom.rb +0 -14
- data/sample/optcarrot/vendor/optcarrot/nes.rb +0 -105
- data/sample/optcarrot/vendor/optcarrot/opt.rb +0 -168
- data/sample/optcarrot/vendor/optcarrot/pad.rb +0 -92
- data/sample/optcarrot/vendor/optcarrot/palette.rb +0 -65
- data/sample/optcarrot/vendor/optcarrot/ppu.rb +0 -1468
- data/sample/optcarrot/vendor/optcarrot/rom.rb +0 -143
- data/sample/optcarrot/vendor/optcarrot.rb +0 -14
- data/sample/optcarrot.rb +0 -354
- data/sample/paint/assets/bucket.png +0 -0
- data/sample/paint/assets/cursor.png +0 -0
- data/sample/paint/assets/eraser.png +0 -0
- data/sample/paint/assets/pencil.png +0 -0
- data/sample/paint/assets/spray.png +0 -0
- data/sample/paint/layer.rb +0 -255
- data/sample/paint/layer_manager.rb +0 -179
- data/sample/paint/paint_demo.rb +0 -837
- data/sample/paint/sparse_pixel_buffer.rb +0 -202
- data/sample/sdl2_demo.rb +0 -318
- data/sample/threading_demo.rb +0 -494
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# A manager class for drivers (user frontend)
|
|
3
|
-
module Driver
|
|
4
|
-
DRIVER_DB = {
|
|
5
|
-
video: { none: :Video },
|
|
6
|
-
audio: { none: :Audio },
|
|
7
|
-
input: { none: :Input },
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
def load(conf)
|
|
13
|
-
video = load_each(conf, :video, conf.video).new(conf)
|
|
14
|
-
audio = load_each(conf, :audio, conf.audio).new(conf)
|
|
15
|
-
input = load_each(conf, :input, conf.input).new(conf, video)
|
|
16
|
-
return video, audio, input
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def load_each(conf, type, name)
|
|
20
|
-
if name
|
|
21
|
-
klass = DRIVER_DB[type][name]
|
|
22
|
-
raise "unknown #{ type } driver: #{ name }" unless klass
|
|
23
|
-
require_relative "driver/#{ name }_#{ type }" unless name == :none
|
|
24
|
-
conf.debug("`#{ name }' #{ type } driver is selected")
|
|
25
|
-
Optcarrot.const_get(klass)
|
|
26
|
-
else
|
|
27
|
-
selected = nil
|
|
28
|
-
DRIVER_DB[type].each_key do |n|
|
|
29
|
-
begin
|
|
30
|
-
selected = load_each(conf, type, n)
|
|
31
|
-
break
|
|
32
|
-
rescue LoadError
|
|
33
|
-
conf.debug("fail to use `#{ n }' #{ type } driver")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
selected
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# A base class of video output driver
|
|
42
|
-
class Video
|
|
43
|
-
WIDTH = 256
|
|
44
|
-
TV_WIDTH = 292
|
|
45
|
-
HEIGHT = 224
|
|
46
|
-
|
|
47
|
-
def initialize(conf)
|
|
48
|
-
@conf = conf
|
|
49
|
-
@palette_rgb = @conf.nestopia_palette ? Palette.nestopia_palette : Palette.defacto_palette
|
|
50
|
-
@palette = [*0..4096] # dummy palette
|
|
51
|
-
init
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
attr_reader :palette
|
|
55
|
-
|
|
56
|
-
def init
|
|
57
|
-
@times = []
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def dispose
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def tick(_output)
|
|
64
|
-
@times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
65
|
-
@times.shift if @times.size > 10
|
|
66
|
-
@times.size < 2 ? 0 : ((@times.last - @times.first) / (@times.size - 1)) ** -1
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def change_window_size(_scale)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def on_resize(_width, _height)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# A base class of audio output driver
|
|
77
|
-
class Audio
|
|
78
|
-
PACK_FORMAT = { 8 => "c*", 16 => "v*" }
|
|
79
|
-
BUFFER_IN_FRAME = 3 # keep audio buffer during this number of frames
|
|
80
|
-
|
|
81
|
-
def initialize(conf)
|
|
82
|
-
@conf = conf
|
|
83
|
-
@rate = conf.audio_sample_rate
|
|
84
|
-
@bits = conf.audio_bit_depth
|
|
85
|
-
raise "sample bits must be 8 or 16" unless @bits == 8 || @bits == 16
|
|
86
|
-
@pack_format = PACK_FORMAT[@bits]
|
|
87
|
-
|
|
88
|
-
init
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def spec
|
|
92
|
-
return @rate, @bits
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def init
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def dispose
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def tick(_output)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# A base class of input driver
|
|
106
|
-
class Input
|
|
107
|
-
def initialize(conf, video)
|
|
108
|
-
@conf = conf
|
|
109
|
-
@video = video
|
|
110
|
-
init
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def init
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def dispose
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def tick(_frame, _pads)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def event(pads, type, code, player)
|
|
123
|
-
case code
|
|
124
|
-
when :start then pads.send(type, player, Pad::START)
|
|
125
|
-
when :select then pads.send(type, player, Pad::SELECT)
|
|
126
|
-
when :a then pads.send(type, player, Pad::A)
|
|
127
|
-
when :b then pads.send(type, player, Pad::B)
|
|
128
|
-
when :right then pads.send(type, player, Pad::RIGHT)
|
|
129
|
-
when :left then pads.send(type, player, Pad::LEFT)
|
|
130
|
-
when :down then pads.send(type, player, Pad::DOWN)
|
|
131
|
-
when :up then pads.send(type, player, Pad::UP)
|
|
132
|
-
else
|
|
133
|
-
return if type != :keydown
|
|
134
|
-
case code
|
|
135
|
-
when :screen_x1 then @video.change_window_size(1)
|
|
136
|
-
when :screen_x2 then @video.change_window_size(2)
|
|
137
|
-
when :screen_x3 then @video.change_window_size(3)
|
|
138
|
-
when :screen_full then @video.change_window_size(nil)
|
|
139
|
-
when :quit then exit
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# CNROM mapper: http://wiki.nesdev.com/w/index.php/CNROM
|
|
3
|
-
class CNROM < ROM
|
|
4
|
-
MAPPER_DB[0x03] = self
|
|
5
|
-
|
|
6
|
-
def reset
|
|
7
|
-
@cpu.add_mappings(0x8000..0xffff, @prg_ref, @chr_ram ? nil : method(:poke_8000))
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def poke_8000(_addr, data)
|
|
11
|
-
@chr_ref.replace(@chr_banks[data & 3])
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# MMC1 mapper: http://wiki.nesdev.com/w/index.php/MMC1
|
|
3
|
-
class MMC1 < ROM
|
|
4
|
-
MAPPER_DB[0x01] = self
|
|
5
|
-
|
|
6
|
-
NMT_MODE = [:first, :second, :vertical, :horizontal]
|
|
7
|
-
PRG_MODE = [:conseq, :conseq, :fix_first, :fix_last]
|
|
8
|
-
CHR_MODE = [:conseq, :noconseq]
|
|
9
|
-
|
|
10
|
-
def init
|
|
11
|
-
@nmt_mode = @prg_mode = @chr_mode = nil
|
|
12
|
-
@prg_bank = @chr_bank_0 = @chr_bank_1 = 0
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def reset
|
|
16
|
-
@shift = @shift_count = 0
|
|
17
|
-
|
|
18
|
-
@chr_banks = @chr_banks.flatten.each_slice(0x1000).to_a
|
|
19
|
-
|
|
20
|
-
@wrk_readable = @wrk_writable = true
|
|
21
|
-
@cpu.add_mappings(0x6000..0x7fff, method(:peek_6000), method(:poke_6000))
|
|
22
|
-
@cpu.add_mappings(0x8000..0xffff, @prg_ref, method(:poke_prg))
|
|
23
|
-
|
|
24
|
-
update_nmt(:horizontal)
|
|
25
|
-
update_prg(:fix_last, 0, 0)
|
|
26
|
-
update_chr(:conseq, 0, 0)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def poke_prg(addr, val)
|
|
30
|
-
if val[7] == 1
|
|
31
|
-
@shift = @shift_count = 0
|
|
32
|
-
else
|
|
33
|
-
@shift |= val[0] << @shift_count
|
|
34
|
-
@shift_count += 1
|
|
35
|
-
if @shift_count == 0x05
|
|
36
|
-
case (addr >> 13) & 0x3
|
|
37
|
-
when 0 # control
|
|
38
|
-
nmt_mode = NMT_MODE[@shift & 3]
|
|
39
|
-
prg_mode = PRG_MODE[@shift >> 2 & 3]
|
|
40
|
-
chr_mode = CHR_MODE[@shift >> 4 & 1]
|
|
41
|
-
update_nmt(nmt_mode)
|
|
42
|
-
update_prg(prg_mode, @prg_bank, @chr_bank_0)
|
|
43
|
-
update_chr(chr_mode, @chr_bank_0, @chr_bank_1)
|
|
44
|
-
when 1 # change chr_bank_0
|
|
45
|
-
# update_prg might modify @chr_bank_0 and prevent updating chr bank,
|
|
46
|
-
# so keep current value.
|
|
47
|
-
bak_chr_bank_0 = @chr_bank_0
|
|
48
|
-
update_prg(@prg_mode, @prg_bank, @shift)
|
|
49
|
-
@chr_bank_0 = bak_chr_bank_0
|
|
50
|
-
update_chr(@chr_mode, @shift, @chr_bank_1)
|
|
51
|
-
when 2 # change chr_bank_1
|
|
52
|
-
update_chr(@chr_mode, @chr_bank_0, @shift)
|
|
53
|
-
when 3 # change png_bank
|
|
54
|
-
update_prg(@prg_mode, @shift, @chr_bank_0)
|
|
55
|
-
end
|
|
56
|
-
@shift = @shift_count = 0
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def update_nmt(nmt_mode)
|
|
62
|
-
return if @nmt_mode == nmt_mode
|
|
63
|
-
@nmt_mode = nmt_mode
|
|
64
|
-
@ppu.nametables = @nmt_mode
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def update_prg(prg_mode, prg_bank, chr_bank_0)
|
|
68
|
-
return if prg_mode == @prg_mode && prg_bank == @prg_bank && chr_bank_0 == @chr_bank_0
|
|
69
|
-
@prg_mode, @prg_bank, @chr_bank_0 = prg_mode, prg_bank, chr_bank_0
|
|
70
|
-
|
|
71
|
-
high_bit = chr_bank_0 & (0x10 & (@prg_banks.size - 1))
|
|
72
|
-
prg_bank_ex = ((@prg_bank & 0x0f) | high_bit) & (@prg_banks.size - 1)
|
|
73
|
-
case @prg_mode
|
|
74
|
-
when :conseq
|
|
75
|
-
lower = prg_bank_ex & ~1
|
|
76
|
-
upper = lower + 1
|
|
77
|
-
when :fix_first
|
|
78
|
-
lower = 0
|
|
79
|
-
upper = prg_bank_ex
|
|
80
|
-
when :fix_last
|
|
81
|
-
lower = prg_bank_ex
|
|
82
|
-
upper = ((@prg_banks.size - 1) & 0x0f) | high_bit
|
|
83
|
-
end
|
|
84
|
-
@prg_ref[0x8000, 0x4000] = @prg_banks[lower]
|
|
85
|
-
@prg_ref[0xc000, 0x4000] = @prg_banks[upper]
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def update_chr(chr_mode, chr_bank_0, chr_bank_1)
|
|
89
|
-
return if chr_mode == @chr_mode && chr_bank_0 == @chr_bank_0 && chr_bank_1 == @chr_bank_1
|
|
90
|
-
@chr_mode, @chr_bank_0, @chr_bank_1 = chr_mode, chr_bank_0, chr_bank_1
|
|
91
|
-
return if @chr_ram
|
|
92
|
-
|
|
93
|
-
@ppu.update(0)
|
|
94
|
-
if @chr_mode == :conseq
|
|
95
|
-
lower = @chr_bank_0 & 0x1e
|
|
96
|
-
upper = lower + 1
|
|
97
|
-
else
|
|
98
|
-
lower = @chr_bank_0
|
|
99
|
-
upper = @chr_bank_1
|
|
100
|
-
end
|
|
101
|
-
@chr_ref[0x0000, 0x1000] = @chr_banks[lower]
|
|
102
|
-
@chr_ref[0x1000, 0x1000] = @chr_banks[upper]
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# MMC3 mapper: http://wiki.nesdev.com/w/index.php/MMC3
|
|
3
|
-
class MMC3 < ROM
|
|
4
|
-
MAPPER_DB[0x04] = self
|
|
5
|
-
|
|
6
|
-
def init(rev = :B) # rev = :A or :B or :C
|
|
7
|
-
@persistant = rev != :A
|
|
8
|
-
|
|
9
|
-
@prg_banks = @prg_banks.flatten.each_slice(0x2000).to_a
|
|
10
|
-
@prg_bank_swap = false
|
|
11
|
-
|
|
12
|
-
@chr_banks = @chr_banks.flatten.each_slice(0x0400).to_a
|
|
13
|
-
@chr_bank_mapping = [nil] * 8
|
|
14
|
-
@chr_bank_swap = false
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def reset
|
|
18
|
-
@wrk_readable = true
|
|
19
|
-
@wrk_writable = false
|
|
20
|
-
|
|
21
|
-
poke_a000 = @mirroring != :FourScreen ? method(:poke_a000) : nil
|
|
22
|
-
@cpu.add_mappings(0x6000..0x7fff, method(:peek_6000), method(:poke_6000))
|
|
23
|
-
@cpu.add_mappings(0x8000.step(0x9fff, 2), @prg_ref, method(:poke_8000))
|
|
24
|
-
@cpu.add_mappings(0x8001.step(0x9fff, 2), @prg_ref, method(:poke_8001))
|
|
25
|
-
@cpu.add_mappings(0xa000.step(0xbfff, 2), @prg_ref, poke_a000)
|
|
26
|
-
@cpu.add_mappings(0xa001.step(0xbfff, 2), @prg_ref, method(:poke_a001))
|
|
27
|
-
@cpu.add_mappings(0xc000.step(0xdfff, 2), @prg_ref, method(:poke_c000))
|
|
28
|
-
@cpu.add_mappings(0xc001.step(0xdfff, 2), @prg_ref, method(:poke_c001))
|
|
29
|
-
@cpu.add_mappings(0xe000.step(0xffff, 2), @prg_ref, method(:poke_e000))
|
|
30
|
-
@cpu.add_mappings(0xe001.step(0xffff, 2), @prg_ref, method(:poke_e001))
|
|
31
|
-
|
|
32
|
-
update_prg(0x8000, 0)
|
|
33
|
-
update_prg(0xa000, 1)
|
|
34
|
-
update_prg(0xc000, -2)
|
|
35
|
-
update_prg(0xe000, -1)
|
|
36
|
-
8.times {|i| update_chr(i * 0x400, i) }
|
|
37
|
-
|
|
38
|
-
@clock = 0
|
|
39
|
-
@hold = PPU::RP2C02_CC * 16
|
|
40
|
-
@ppu.monitor_a12_rising_edge(self)
|
|
41
|
-
@cpu.ppu_sync = true
|
|
42
|
-
|
|
43
|
-
@count = 0
|
|
44
|
-
@latch = 0
|
|
45
|
-
@reload = false
|
|
46
|
-
@enabled = false
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# prg_bank_swap = F T
|
|
50
|
-
# 0x8000..0x9fff: 0 2
|
|
51
|
-
# 0xa000..0xbfff: 1 1
|
|
52
|
-
# 0xc000..0xdfff: 2 0
|
|
53
|
-
# 0xe000..0xffff: 3 3
|
|
54
|
-
def update_prg(addr, bank)
|
|
55
|
-
bank %= @prg_banks.size
|
|
56
|
-
addr ^= 0x4000 if @prg_bank_swap && addr[13] == 0
|
|
57
|
-
@prg_ref[addr, 0x2000] = @prg_banks[bank]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def update_chr(addr, bank)
|
|
61
|
-
return if @chr_ram
|
|
62
|
-
idx = addr / 0x400
|
|
63
|
-
bank %= @chr_banks.size
|
|
64
|
-
return if @chr_bank_mapping[idx] == bank
|
|
65
|
-
addr ^= 0x1000 if @chr_bank_swap
|
|
66
|
-
@ppu.update(0)
|
|
67
|
-
@chr_ref[addr, 0x400] = @chr_banks[bank]
|
|
68
|
-
@chr_bank_mapping[idx] = bank
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def poke_8000(_addr, data)
|
|
72
|
-
@reg_select = data & 7
|
|
73
|
-
prg_bank_swap = data[6] == 1
|
|
74
|
-
chr_bank_swap = data[7] == 1
|
|
75
|
-
|
|
76
|
-
if prg_bank_swap != @prg_bank_swap
|
|
77
|
-
@prg_bank_swap = prg_bank_swap
|
|
78
|
-
@prg_ref[0x8000, 0x2000], @prg_ref[0xc000, 0x2000] = @prg_ref[0xc000, 0x2000], @prg_ref[0x8000, 0x2000]
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
if chr_bank_swap != @chr_bank_swap
|
|
82
|
-
@chr_bank_swap = chr_bank_swap
|
|
83
|
-
unless @chr_ram
|
|
84
|
-
@ppu.update(0)
|
|
85
|
-
@chr_ref.rotate!(0x1000)
|
|
86
|
-
@chr_bank_mapping.rotate!(4)
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def poke_8001(_addr, data)
|
|
92
|
-
if @reg_select < 6
|
|
93
|
-
if @reg_select < 2
|
|
94
|
-
update_chr(@reg_select * 0x0800, data & 0xfe)
|
|
95
|
-
update_chr(@reg_select * 0x0800 + 0x0400, data | 0x01)
|
|
96
|
-
else
|
|
97
|
-
update_chr((@reg_select - 2) * 0x0400 + 0x1000, data)
|
|
98
|
-
end
|
|
99
|
-
else
|
|
100
|
-
update_prg((@reg_select - 6) * 0x2000 + 0x8000, data & 0x3f)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def poke_a000(_addr, data)
|
|
105
|
-
@ppu.nametables = data[0] == 1 ? :horizontal : :vertical
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def poke_a001(_addr, data)
|
|
109
|
-
@wrk_readable = data[7] == 1
|
|
110
|
-
@wrk_writable = data[6] == 0 && @wrk_readable
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def poke_c000(_addr, data)
|
|
114
|
-
@ppu.update(0)
|
|
115
|
-
@latch = data
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def poke_c001(_addr, _data)
|
|
119
|
-
@ppu.update(0)
|
|
120
|
-
@reload = true
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def poke_e000(_addr, _data)
|
|
124
|
-
@ppu.update(0)
|
|
125
|
-
@enabled = false
|
|
126
|
-
@cpu.clear_irq(CPU::IRQ_EXT)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def poke_e001(_addr, _data)
|
|
130
|
-
@ppu.update(0)
|
|
131
|
-
@enabled = true
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def vsync
|
|
135
|
-
@clock = @clock > @cpu.next_frame_clock ? @clock - @cpu.next_frame_clock : 0
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def a12_signaled(cycle)
|
|
139
|
-
clk, @clock = @clock, cycle + @hold
|
|
140
|
-
return if cycle < clk
|
|
141
|
-
flag = @persistant || @count > 0
|
|
142
|
-
if @reload
|
|
143
|
-
@reload = false
|
|
144
|
-
@count = @latch
|
|
145
|
-
elsif @count == 0
|
|
146
|
-
@count = @latch
|
|
147
|
-
else
|
|
148
|
-
@count -= 1
|
|
149
|
-
end
|
|
150
|
-
@cpu.do_irq(CPU::IRQ_EXT, cycle) if flag && @count == 0 && @enabled
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# UxROM mapper: http://wiki.nesdev.com/w/index.php/UxROM
|
|
3
|
-
class UxROM < ROM
|
|
4
|
-
MAPPER_DB[0x02] = self
|
|
5
|
-
|
|
6
|
-
def reset
|
|
7
|
-
@cpu.add_mappings(0x8000..0xffff, @prg_ref, method(:poke_8000))
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def poke_8000(_addr, data)
|
|
11
|
-
@prg_ref[0x8000, 0x4000] = @prg_banks[data & 7]
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
FOREVER_CLOCK = 0xffffffff
|
|
3
|
-
RP2A03_CC = 12
|
|
4
|
-
|
|
5
|
-
# NES emulation main
|
|
6
|
-
class NES
|
|
7
|
-
FPS = 60
|
|
8
|
-
|
|
9
|
-
def initialize(conf = ARGV)
|
|
10
|
-
@conf = Config.new(conf)
|
|
11
|
-
|
|
12
|
-
@video, @audio, @input = Driver.load(@conf)
|
|
13
|
-
|
|
14
|
-
@cpu = CPU.new(@conf)
|
|
15
|
-
@apu = @cpu.apu = APU.new(@conf, @cpu, *@audio.spec)
|
|
16
|
-
@ppu = @cpu.ppu = PPU.new(@conf, @cpu, @video.palette)
|
|
17
|
-
@rom = ROM.load(@conf, @cpu, @ppu)
|
|
18
|
-
@pads = Pads.new(@conf, @cpu, @apu)
|
|
19
|
-
|
|
20
|
-
@frame = 0
|
|
21
|
-
@frame_target = @conf.frames == 0 ? nil : @conf.frames
|
|
22
|
-
@fps_history = [] if save_fps_history?
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def inspect
|
|
26
|
-
"#<#{ self.class }>"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
attr_reader :fps, :video, :audio, :input, :cpu, :ppu, :apu
|
|
30
|
-
|
|
31
|
-
def reset
|
|
32
|
-
@cpu.reset
|
|
33
|
-
@apu.reset
|
|
34
|
-
@ppu.reset
|
|
35
|
-
@rom.reset
|
|
36
|
-
@pads.reset
|
|
37
|
-
@cpu.boot
|
|
38
|
-
@rom.load_battery
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def step
|
|
42
|
-
@ppu.setup_frame
|
|
43
|
-
@cpu.run
|
|
44
|
-
@ppu.vsync
|
|
45
|
-
@apu.vsync
|
|
46
|
-
@cpu.vsync
|
|
47
|
-
@rom.vsync
|
|
48
|
-
|
|
49
|
-
@input.tick(@frame, @pads)
|
|
50
|
-
@fps = @video.tick(@ppu.output_pixels)
|
|
51
|
-
@fps_history << @fps if save_fps_history?
|
|
52
|
-
@audio.tick(@apu.output)
|
|
53
|
-
|
|
54
|
-
@frame += 1
|
|
55
|
-
@conf.info("frame #{ @frame }") if @conf.loglevel >= 2
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def dispose
|
|
59
|
-
if @fps
|
|
60
|
-
@conf.info("fps: %.2f (in the last 10 frames)" % @fps)
|
|
61
|
-
if @conf.print_fps_history
|
|
62
|
-
puts "frame,fps-history"
|
|
63
|
-
@fps_history.each_with_index {|fps, frame| puts "#{ frame },#{ fps }" }
|
|
64
|
-
end
|
|
65
|
-
if @conf.print_p95fps
|
|
66
|
-
puts "p95 fps: #{ @fps_history.sort[(@fps_history.length * 0.05).floor] }"
|
|
67
|
-
end
|
|
68
|
-
puts "fps: #{ @fps }" if @conf.print_fps
|
|
69
|
-
end
|
|
70
|
-
if @conf.print_video_checksum && @video.instance_of?(Video)
|
|
71
|
-
puts "checksum: #{ @ppu.output_pixels.pack("C*").sum }"
|
|
72
|
-
end
|
|
73
|
-
@video.dispose
|
|
74
|
-
@audio.dispose
|
|
75
|
-
@input.dispose
|
|
76
|
-
@rom.save_battery
|
|
77
|
-
@ppu.dispose
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def run
|
|
81
|
-
reset
|
|
82
|
-
|
|
83
|
-
if @conf.stackprof_mode
|
|
84
|
-
require "stackprof"
|
|
85
|
-
out = @conf.stackprof_output.sub("MODE", @conf.stackprof_mode)
|
|
86
|
-
StackProf.start(mode: @conf.stackprof_mode.to_sym, out: out, raw: true)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
step until @frame == @frame_target
|
|
90
|
-
|
|
91
|
-
if @conf.stackprof_mode
|
|
92
|
-
StackProf.stop
|
|
93
|
-
StackProf.results
|
|
94
|
-
end
|
|
95
|
-
ensure
|
|
96
|
-
dispose
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
private
|
|
100
|
-
|
|
101
|
-
def save_fps_history?
|
|
102
|
-
@conf.print_fps_history || @conf.print_p95fps
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
module Optcarrot
|
|
2
|
-
# dirty methods manipulating and generating methods...
|
|
3
|
-
module CodeOptimizationHelper
|
|
4
|
-
def initialize(loglevel, enabled_opts)
|
|
5
|
-
@loglevel = loglevel
|
|
6
|
-
options = self.class::OPTIONS
|
|
7
|
-
opts = {}
|
|
8
|
-
enabled_opts ||= [:all]
|
|
9
|
-
default =
|
|
10
|
-
(enabled_opts == [:all] || enabled_opts != [] && enabled_opts.all? {|opt| opt.to_s.start_with?("-") })
|
|
11
|
-
options.each {|opt| opts[opt] = default }
|
|
12
|
-
(enabled_opts - [:none, :all]).each do |opt|
|
|
13
|
-
val = true
|
|
14
|
-
if opt.to_s.start_with?("-")
|
|
15
|
-
opt = opt.to_s[1..-1].to_sym
|
|
16
|
-
val = false
|
|
17
|
-
end
|
|
18
|
-
raise "unknown optimization: `#{ opt }'" unless options.include?(opt)
|
|
19
|
-
opts[opt] = val
|
|
20
|
-
end
|
|
21
|
-
options.each {|opt| instance_variable_set(:"@#{ opt }", opts[opt]) }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def depends(opt, depended_opt)
|
|
25
|
-
if instance_variable_get(:"@#{ opt }") && !instance_variable_get(:"@#{ depended_opt }")
|
|
26
|
-
raise "`#{ opt }' depends upon `#{ depended_opt }'"
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def gen(*codes)
|
|
31
|
-
codes.map {|code| code.to_s.chomp }.join("\n") + "\n"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# change indent
|
|
35
|
-
def indent(i, code)
|
|
36
|
-
if i > 0
|
|
37
|
-
code.gsub(/^(.+)$/) { " " * i + $1 }
|
|
38
|
-
elsif i < 0
|
|
39
|
-
code.gsub(/^ {#{ -i }}/, "")
|
|
40
|
-
else
|
|
41
|
-
code
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# generate a branch
|
|
46
|
-
def branch(cond, code1, code2)
|
|
47
|
-
gen(
|
|
48
|
-
"if #{ cond }",
|
|
49
|
-
indent(2, code1),
|
|
50
|
-
"else",
|
|
51
|
-
indent(2, code2),
|
|
52
|
-
"end",
|
|
53
|
-
)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
MethodDef = Struct.new(:params, :body)
|
|
57
|
-
|
|
58
|
-
METHOD_DEFINITIONS_RE = /
|
|
59
|
-
^(\ +)def\s+(\w+)(?:\((.*)\))?\n
|
|
60
|
-
^((?:\1\ +.*\n|\n)*)
|
|
61
|
-
^\1end$
|
|
62
|
-
/x
|
|
63
|
-
# extract all method definitions
|
|
64
|
-
def parse_method_definitions(file)
|
|
65
|
-
src = File.read(file)
|
|
66
|
-
mdefs = {}
|
|
67
|
-
src.scan(METHOD_DEFINITIONS_RE) do |indent, meth, params, body|
|
|
68
|
-
body = indent(-indent.size - 2, body)
|
|
69
|
-
|
|
70
|
-
# noramlize: break `when ... then`
|
|
71
|
-
body = body.gsub(/^( *)when +(.*?) +then +(.*)/) { $1 + "when #{ $2 }\n" + $1 + " " + $3 }
|
|
72
|
-
|
|
73
|
-
# normalize: return unless
|
|
74
|
-
body = "if " + $1 + indent(2, $') + "end\n" if body =~ /\Areturn unless (.*)/
|
|
75
|
-
|
|
76
|
-
# normalize: if modifier -> if statement
|
|
77
|
-
nil while body.gsub!(/^( *)((?!#)\S.*) ((?:if|unless) .*\n)/) { indent($1.size, gen($3, " " + $2, "end")) }
|
|
78
|
-
|
|
79
|
-
mdefs[meth.to_sym] = MethodDef[params ? params.split(", ") : nil, body]
|
|
80
|
-
end
|
|
81
|
-
mdefs
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# inline method calls with no arguments
|
|
85
|
-
def expand_methods(code, mdefs, meths = mdefs.keys)
|
|
86
|
-
code.gsub(/^( *)\b(#{ meths * "|" })\b(?:\((.*?)\))?\n/) do
|
|
87
|
-
indent, meth, args = $1, $2, $3
|
|
88
|
-
body = mdefs[meth.to_sym]
|
|
89
|
-
body = body.body if body.is_a?(MethodDef)
|
|
90
|
-
if args
|
|
91
|
-
mdefs[meth.to_sym].params.zip(args.split(", ")) do |param, arg|
|
|
92
|
-
body = replace_var(body, param, arg)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
indent(indent.size, body)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def expand_inline_methods(code, meth, mdef)
|
|
100
|
-
code.gsub(/\b#{ meth }\b(?:\(((?:@?\w+, )*@?\w+)\))?/) do
|
|
101
|
-
args = $1
|
|
102
|
-
b = "(#{ mdef.body.chomp.gsub(/ *#.*/, "").gsub("\n", "; ") })"
|
|
103
|
-
if args
|
|
104
|
-
mdef.params.zip(args.split(", ")) do |param, arg|
|
|
105
|
-
b = replace_var(b, param, arg)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
b
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def replace_var(code, var, bool)
|
|
113
|
-
re = var.start_with?("@") ? /#{ var }\b/ : /\b#{ var }\b/
|
|
114
|
-
code.gsub(re) { bool }
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def replace_cond_var(code, var, bool)
|
|
118
|
-
code.gsub(/(if|unless)\s#{ var }\b/) { $1 + " " + bool }
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
TRIVIAL_BRANCH_RE = /
|
|
122
|
-
^(\ *)(if|unless)\ (true|false)\n
|
|
123
|
-
^((?:\1\ +.*\n|\n)*)
|
|
124
|
-
(?:
|
|
125
|
-
\1else\n
|
|
126
|
-
((?:\1\ +.*\n|\n)*)
|
|
127
|
-
)?
|
|
128
|
-
^\1end\n
|
|
129
|
-
/x
|
|
130
|
-
# remove "if true" or "if false"
|
|
131
|
-
def remove_trivial_branches(code)
|
|
132
|
-
code = code.dup
|
|
133
|
-
nil while
|
|
134
|
-
code.gsub!(TRIVIAL_BRANCH_RE) do
|
|
135
|
-
if ($2 == "if") == ($3 == "true")
|
|
136
|
-
indent(-2, $4)
|
|
137
|
-
else
|
|
138
|
-
$5 ? indent(-2, $5) : ""
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
code
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# replace instance variables with temporal local variables
|
|
145
|
-
# CAUTION: the instance variable must not be accessed out of CPU#run
|
|
146
|
-
def localize_instance_variables(code, ivars = code.scan(/@\w+/).uniq.sort)
|
|
147
|
-
ivars = ivars.map {|ivar| ivar.to_s[1..-1] }
|
|
148
|
-
|
|
149
|
-
inits, finals = [], []
|
|
150
|
-
ivars.each do |ivar|
|
|
151
|
-
lvar = "__#{ ivar }__"
|
|
152
|
-
inits << "#{ lvar } = @#{ ivar }"
|
|
153
|
-
finals << "@#{ ivar } = #{ lvar }"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
code = code.gsub(/@(#{ ivars * "|" })\b/) { "__#{ $1 }__" }
|
|
157
|
-
|
|
158
|
-
gen(
|
|
159
|
-
"begin",
|
|
160
|
-
indent(2, inits.join("\n")),
|
|
161
|
-
indent(2, code),
|
|
162
|
-
"ensure",
|
|
163
|
-
indent(2, finals.join("\n")),
|
|
164
|
-
"end",
|
|
165
|
-
)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|