burn 0.1.3 → 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 (56) hide show
  1. data/README.md +56 -569
  2. data/lib/burn.rb +5 -3
  3. data/lib/burn/cli.rb +160 -199
  4. data/lib/burn/config.rb +4 -0
  5. data/lib/burn/config/app.rb +15 -0
  6. data/lib/burn/config/config_base.rb +16 -0
  7. data/lib/burn/config/loader.rb +23 -0
  8. data/lib/burn/config/server.rb +11 -0
  9. data/lib/burn/fuel.rb +11 -0
  10. data/lib/burn/{dsl → fuel}/dsl_base.rb +1 -1
  11. data/lib/burn/fuel/rom/channel_stream.rb +106 -0
  12. data/lib/burn/fuel/rom/declare.rb +43 -0
  13. data/lib/burn/fuel/rom/music.rb +37 -0
  14. data/lib/burn/fuel/rom/note.rb +126 -0
  15. data/lib/burn/fuel/rom/scene.rb +279 -0
  16. data/lib/burn/fuel/rom/sound.rb +31 -0
  17. data/lib/burn/fuel/rom/sound_effect.rb +92 -0
  18. data/lib/burn/fuel/telnet/declare.rb +26 -0
  19. data/lib/burn/fuel/telnet/scene.rb +112 -0
  20. data/lib/burn/generator.rb +10 -3
  21. data/lib/burn/generator/rom/assembly_music.rb +49 -0
  22. data/lib/burn/generator/rom/assembly_sound_effect.rb +35 -0
  23. data/lib/burn/generator/rom/c_source.rb +55 -0
  24. data/lib/burn/generator/{template → rom/template}/mus_template.s +0 -0
  25. data/lib/burn/generator/{template → rom/template}/template.c +0 -0
  26. data/lib/burn/generator/rom_builder.rb +67 -0
  27. data/lib/burn/generator/telnet/jit_compiler.rb +41 -0
  28. data/lib/burn/generator/telnet/screen.rb +86 -0
  29. data/lib/burn/generator/telnet/sprite.rb +16 -0
  30. data/lib/burn/generator/telnet/user_input.rb +25 -0
  31. data/lib/burn/generator/telnet_vm.rb +50 -0
  32. data/lib/burn/pxes.rb +3 -0
  33. data/lib/burn/{util/pxes.rb → pxes/cc65_transpiler.rb} +8 -14
  34. data/lib/burn/pxes/cruby_transpiler.rb +189 -0
  35. data/lib/burn/pxes/transpiler_base.rb +14 -0
  36. data/lib/burn/server.rb +2 -0
  37. data/lib/burn/server/rom.rb +24 -0
  38. data/lib/burn/server/telnet.rb +120 -0
  39. data/lib/burn/util.rb +1 -2
  40. data/lib/burn/util/logo.rb +68 -0
  41. data/lib/burn/util/os.rb +1 -1
  42. data/lib/burn/version.rb +1 -1
  43. metadata +53 -19
  44. data/lib/burn/builder.rb +0 -65
  45. data/lib/burn/dsl.rb +0 -8
  46. data/lib/burn/dsl/channel_stream.rb +0 -105
  47. data/lib/burn/dsl/declare.rb +0 -42
  48. data/lib/burn/dsl/music.rb +0 -36
  49. data/lib/burn/dsl/note.rb +0 -125
  50. data/lib/burn/dsl/scene.rb +0 -278
  51. data/lib/burn/dsl/sound.rb +0 -30
  52. data/lib/burn/dsl/sound_effect.rb +0 -91
  53. data/lib/burn/generator/assembly_music.rb +0 -47
  54. data/lib/burn/generator/assembly_sound_effect.rb +0 -33
  55. data/lib/burn/generator/c_source.rb +0 -54
  56. data/lib/burn/util/server.rb +0 -21
@@ -0,0 +1,23 @@
1
+ module Burn
2
+ module Configuration
3
+ class Loader
4
+ attr_reader :server, :app
5
+
6
+ def initialize(code)
7
+ @server = Server.new
8
+ @app = App.new
9
+ self.instance_eval code
10
+ end
11
+
12
+ def method_missing(method_symbol, *args, &block)
13
+ if method_symbol == :config then
14
+ if args[0] == :server then
15
+ @server.instance_eval &block
16
+ elsif args[0] == :app then
17
+ @app.instance_eval &block
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module Burn
2
+ module Configuration
3
+ class Server < ConfigBase
4
+ def initialize
5
+ @ip_addr = '127.0.0.1'
6
+ @port = 60000
7
+ @max_clients = 1
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/burn/fuel.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'burn/fuel/dsl_base'
2
+ require 'burn/fuel/rom/sound_effect'
3
+ require 'burn/fuel/rom/note'
4
+ require 'burn/fuel/rom/channel_stream'
5
+ require 'burn/fuel/rom/declare'
6
+ require 'burn/fuel/rom/sound'
7
+ require 'burn/fuel/rom/music'
8
+ require 'burn/fuel/rom/scene'
9
+
10
+ require 'burn/fuel/telnet/declare'
11
+ require 'burn/fuel/telnet/scene'
@@ -1,5 +1,5 @@
1
1
  module Burn
2
- module Dsl
2
+ module Fuel
3
3
  class DslBase
4
4
  # blankslate
5
5
  instance_methods.each do |m|
@@ -0,0 +1,106 @@
1
+ module Burn
2
+ module Fuel
3
+ module Rom
4
+ class ChannelStream
5
+ # tempo 193 ~ 211 (18 levels, 202 is median)
6
+ # see: http://ja.wikipedia.org/wiki/%E6%BC%94%E5%A5%8F%E8%A8%98%E5%8F%B7
7
+ TEMPO_BASE = 192
8
+ PRESTO = 1 + TEMPO_BASE
9
+ ALLEGRO = 2 + TEMPO_BASE
10
+ MODERATO = 3 + TEMPO_BASE
11
+ ANDANTE = 4 + TEMPO_BASE
12
+ ADAGIO = 5 + TEMPO_BASE
13
+ LARGO = 6 + TEMPO_BASE
14
+
15
+ # instrument number 64~126 (62steps) *note* no sound for 64, 65~72 seems working right now
16
+ BASS = 65
17
+ PIANO = 66
18
+ STRING = 67
19
+ ARPEGGIO = 68
20
+ STEP = 69
21
+ BELL = 70
22
+ ACOUSTIC = 71
23
+ GUITAR = 72
24
+ THEREMIN = 73
25
+
26
+ def initialize(index, instrument, tempo)
27
+ @index = index
28
+
29
+ @duration_list = [:half, :quarter, :eighth, :sixteenth]
30
+ @duration_numeric_list = {2=>:half, 4=>:quarter, 8=>:eighth, 16=>:sixteenth}
31
+ @duration_symbol_list = [:dotted, :triplet]
32
+ @articulation_list = [:tenuto, :staccato, :accent]
33
+ @instrument_list = [:piano, :bass]
34
+
35
+ @notes = Array.new
36
+ @notes << ChannelStream.const_get(tempo.upcase)
37
+ @notes << ChannelStream.const_get(instrument.upcase)
38
+
39
+ @default_duration = :quarter
40
+ @default_articulation = nil
41
+ @default_pitch = :c0
42
+
43
+ @output_buffer = Array.new
44
+ end
45
+
46
+ def default(*args)
47
+ args.each{ |option|
48
+ if @duration_numeric_list.include?(option) then
49
+ @default_duration = @duration_numeric_list[option]
50
+ elsif @duration_list.include?(option) then
51
+ @default_duration = option
52
+ elsif @articulation_list.include?(option) then
53
+ @default_articulation = option
54
+ end
55
+ }
56
+ end
57
+
58
+ def method_missing(method_symbol, *args, &block)
59
+ case method_symbol
60
+ when :segno then
61
+ @output_buffer << __translate
62
+ @output_buffer << "@chn#{@index.to_s}_segno:"
63
+ @notes = Array.new
64
+ when :dal_segno then
65
+ @output_buffer << __translate
66
+ @output_buffer << " .byte $fe" # end of stream
67
+ @output_buffer << " .word @chn#{@index.to_s}_segno"
68
+ @notes = Array.new
69
+ else
70
+ note = Note.new(@default_duration, method_symbol, @default_articulation)
71
+ args.each{ |option|
72
+ if @duration_numeric_list.include?(option) then
73
+ note.duration = @duration_numeric_list[option]
74
+ elsif @duration_list.include?(option) then
75
+ note.duration = option
76
+ elsif @articulation_list.include?(option) then
77
+ note.articulation = option
78
+ elsif @duration_symbol_list.include?(option) then
79
+ note.duration_symbol = option
80
+ end
81
+ }
82
+ @notes.concat note.generate
83
+ end
84
+ end
85
+
86
+ def load(&block)
87
+ self.instance_eval &block
88
+ end
89
+
90
+ def generate
91
+ if @notes.count>0 then
92
+ @output_buffer << __translate
93
+ end
94
+ @output_buffer.join("\n")
95
+ # " .byte " + @notes.map{|p| "$%02x" % p}.join(",")
96
+ # " .byte $c1,$43,$1d,$80,$1f,$80,$21,$82,$1d,$80,$1d,$80,$1f,$92"
97
+ end
98
+
99
+ private
100
+ def __translate
101
+ " .byte " + @notes.map{|p| "$%02x" % p}.join(",")
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,43 @@
1
+ module Burn
2
+ module Fuel
3
+ module Rom
4
+ class Declare < Fuel::DslBase
5
+
6
+ def initialize(resource_name, context)
7
+ super(resource_name, context)
8
+ end
9
+
10
+ def method_missing(method_symbol, *args, &block)
11
+ log :method_symbol=>method_symbol, :args=>args, block=>block
12
+
13
+ proc = Proc.new{|args|
14
+ key=args.shift
15
+ value=args.shift
16
+ if value.is_a?(String) then
17
+ patternizer = Util::Patternizer.new(value)
18
+
19
+ struct_var = "static sprite_schema #{key}={0, 0, {"
20
+ @pattern_table_index[key.to_sym] = @pattern_table_pointer
21
+ patternizer.patterns.each_with_index do |p, i|
22
+ log p, i, patternizer.height, "aaaaaaaaaaaaaaaaaaa"
23
+ struct_var += sprintf("%d, %d, %#x, 0,", (i % patternizer.width)*8 , (i / (patternizer.patterns.size / patternizer.height) )*8 , @pattern_table_pointer)
24
+ @pattern_tables << p
25
+ @pattern_table_pointer+=1
26
+ end
27
+ struct_var += "128}};"
28
+
29
+ @global.push sprintf(struct_var, "")
30
+
31
+ else
32
+ @global.push "static unsigned char #{key};"
33
+ @code_blocks.push "#{key}="+sprintf("%#x", value)+";"
34
+ log :key=>key, :value=>value.to_s(16), :value_class=>value.class
35
+ end
36
+ }
37
+
38
+ @context.instance_exec [method_symbol.to_s, args[0]], &proc
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Burn
2
+ module Fuel
3
+ module Rom
4
+ class Music < DslBase
5
+ def initialize(resource_name, context)
6
+ super(resource_name, context)
7
+
8
+ @default_tempo = :allegro
9
+
10
+ if resource_name.nil? then
11
+ raise Exception.new "Resource name for Music class must be provided."
12
+ else
13
+ @resource_name = resource_name
14
+ end
15
+
16
+ @context.instance_exec resource_name, &lambda{|resource_name|
17
+ @resources["#{resource_name}"] = Array.new
18
+ }
19
+
20
+ end
21
+
22
+ def channel(instrument, &block)
23
+ @context.instance_exec(@resource_name,@default_tempo) do |resource_name, default_tempo|
24
+ stream = ChannelStream.new(@resources["#{resource_name}"].count, instrument, default_tempo)
25
+ stream.load(&block)
26
+ @resources["#{resource_name}"] << stream.generate
27
+ #@resources["#{resource_name}"] << " .byte $43,$1d,$80,$1f,$80,$21,$82,$1d,$80,$1d,$80,$1f,$92"
28
+ end
29
+ end
30
+
31
+ def tempo(speed)
32
+ @default_tempo = speed
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,126 @@
1
+ module Burn
2
+ module Fuel
3
+ module Rom
4
+ class Note
5
+ # For music
6
+ # (0..59 are octaves 1-5, 63 note stop)
7
+ C = 0
8
+ D = 2
9
+ E = 4
10
+ F = 5
11
+ G = 7
12
+ A = 9
13
+ B = 11
14
+
15
+ CS = 11
16
+ DS = 1
17
+ ES = 3
18
+ FS = 4
19
+ GS = 6
20
+ AS = 8
21
+ BS = 10
22
+
23
+ REST = -1
24
+
25
+
26
+ # Empty Rows 128~191 (63steps)
27
+ DURATION_BASE = 127
28
+ SIXTEENTH = 4 + DURATION_BASE
29
+ EIGHTH = 10 + DURATION_BASE
30
+ QUARTER = 22 + DURATION_BASE
31
+ HALF = 46 + DURATION_BASE
32
+ DOTTED_SIXTEENTH = 7 + DURATION_BASE
33
+ DOTTED_EIGHTH = 16 + DURATION_BASE
34
+ DOTTED_QUARTER = 34 + DURATION_BASE
35
+ DOTTED_HALF = 70 + DURATION_BASE
36
+ TRIPLET_SIXTEENTH = 1 + DURATION_BASE
37
+ TRIPLET_EIGHTH = 4 + DURATION_BASE
38
+ TRIPLET_QUARTER = 10 + DURATION_BASE
39
+ TRIPLET_HALF = 22 + DURATION_BASE
40
+
41
+ TENUTO = 1.0
42
+ ACCENT = 0.7
43
+ STACCATO = 0.2
44
+
45
+ attr_accessor :duration, :articulation, :duration_symbol
46
+
47
+ def initialize(duration=:quarter, pitch=:c0, articulation=nil, duration_symbol=nil)
48
+ @pitch_list = {c:C, bsharp:C,
49
+ ds:DS, dflat:DS, csharp:DS,
50
+ d:D,
51
+ es:ES, eflat:ES, dsharp:DS,
52
+ e:E, fs:FS, fflat:FS,
53
+ f:F, esharp:F,
54
+ gs:GS, gflat:GS, fsharp:GS,
55
+ g:G,
56
+ as:AS, aflat:AS, gsharp:AS,
57
+ a:A,
58
+ bs:BS, bflat:BS, asharp:BS,
59
+ b:B, cs:CS, cflat:CS,
60
+ rest:REST,
61
+ }
62
+
63
+ # Fix pitch first at this early timing
64
+ if !pitch.to_s.match(/(.+)(\d)/).nil? then
65
+ raise Exception.new "pitch #{pitch.to_s} is not defined." if !@pitch_list.keys.include?(Regexp.last_match(1).to_sym)
66
+ @pitch = @pitch_list[Regexp.last_match(1).to_sym]
67
+ if Regexp.last_match(2).to_i <= 4 then
68
+ @octave = Regexp.last_match(2).to_i
69
+ else
70
+ raise Exception.new "Sorry only 0-4 octave range is supported."
71
+ end
72
+ else
73
+ raise Exception.new "pitch #{pitch.to_s} is not defined." if !@pitch_list.keys.include?(pitch)
74
+ @pitch = @pitch_list[pitch]
75
+ @octave = 0
76
+ end
77
+
78
+ @duration = duration
79
+ @articulation = articulation
80
+ @duration_symbol = duration_symbol
81
+ end
82
+
83
+ def generate
84
+ duration = get_actual_duration
85
+
86
+ result = Array.new
87
+ if @pitch==REST then
88
+ @articuulation = 0
89
+ else
90
+ result << @pitch + 12 * @octave
91
+ end
92
+
93
+ articulation = Note.const_get(@articulation.upcase) if !@articulation.nil?
94
+ if articulation.nil? then
95
+ result << duration
96
+ result << 63 # note stop
97
+ elsif articulation == TENUTO then
98
+ result << duration + 1
99
+ else
100
+ if (len(duration)*articulation).ceil > 0 then
101
+ result << DURATION_BASE + (len(duration)*articulation).ceil
102
+ result << 63 # note stop
103
+ end
104
+ if (len(duration)*(1.0 - articulation)).floor > 0 then
105
+ result << DURATION_BASE + (len(duration)*(1.0-articulation)).floor
106
+ end
107
+ end
108
+ result
109
+ end
110
+
111
+ private
112
+ def len(duration)
113
+ duration - DURATION_BASE
114
+ end
115
+
116
+ def get_actual_duration
117
+ if !@duration_symbol.nil? then
118
+ Note.const_get(@duration_symbol.upcase.to_s + "_" + @duration.upcase.to_s)
119
+ else
120
+ Note.const_get(@duration.upcase.to_s)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,279 @@
1
+ module Burn
2
+ module Fuel
3
+ module Rom
4
+ class Scene < DslBase
5
+ # Where to draw
6
+ BG = 0 # background palette (0x00-0x0f)
7
+ TEXT = 1 # background palette (0x00-0x0f)
8
+ PALETTE_X1 = 5 # palette A for #screen and #paint method (whcih is a part background palette)
9
+ PALETTE_X2 = 6
10
+ PALETTE_X3 = 7
11
+ PALETTE_Y1 = 9
12
+ PALETTE_Y2 = 10
13
+ PALETTE_Y3 = 11
14
+ PALETTE_Z1 = 13
15
+ PALETTE_Z2 = 14
16
+ PALETTE_Z3 = 15
17
+ SPRITE = 17 # sprite palette (0x10-0x1f)
18
+
19
+ PALETTE_DEFAULT = 0
20
+ PALETTE_X = 85 # palette expression for PPU Attribute table
21
+ PALETTE_Y = 170 # palette expression for PPU Attribute table
22
+ PALETTE_Z = 255 # palette expression for PPU Attribute table
23
+
24
+ # Color Control
25
+ WHITE = 0
26
+ LIGHTBLUE = 1
27
+ BLUE = 2
28
+ PURPLE = 3
29
+ PINK = 4
30
+ RED = 5
31
+ DEEPRED = 6
32
+ ORANGE = 7
33
+ LIGHTORANGE = 8
34
+ DARKGREEN = 9
35
+ GREEN = 10
36
+ LIGHTGREEN = 11
37
+ BLUEGREEN = 12
38
+ GRAY = 13
39
+ BLACK = 14
40
+
41
+ # Light Control
42
+ DARKEST = 0
43
+ DARKER = 16
44
+ LIGHTER = 32
45
+ LIGHTEST = 48
46
+
47
+ def initialize(resource_name, context)
48
+ super(resource_name, context)
49
+ end
50
+
51
+ def label(string, x=0, y=1)
52
+ @context.instance_exec {@code_blocks.push sprintf("put_str(NTADR(%d,%d),\"%s\");", x, y, string.upcase)}
53
+ end
54
+
55
+ def fade_in
56
+ @context.instance_exec do
57
+ [
58
+ "ppu_on_all();",
59
+ "screen_fade_in();"
60
+ ].each {|p| @code_blocks.push p}
61
+ end
62
+ end
63
+
64
+ def fade_out(sec=3)
65
+ @context.instance_exec {@code_blocks.push "screen_fade_out(#{sec.to_s});"}
66
+ end
67
+
68
+ def color(palette, color, lightness=:lighter)
69
+ palette=Scene.const_get(palette.upcase)
70
+ color=Scene.const_get(color.upcase)
71
+ lightness=Scene.const_get(lightness.upcase)
72
+
73
+ @context.instance_exec {@code_blocks.push "pal_col(#{palette},0x#{(lightness+color).to_s(16)});"}
74
+ end
75
+
76
+ def play(song_title)
77
+ @context.instance_exec do
78
+ @global << "extern const unsigned char music_#{song_title}[];"
79
+ @code_blocks << "music_play(music_#{song_title});"
80
+ end
81
+ end
82
+
83
+ def stop
84
+ @context.instance_exec {@code_blocks.push "music_stop();"}
85
+ end
86
+
87
+ def show(string, x=0, y=0, *args)
88
+ @context.instance_exec(@resource_name) do |resource|
89
+ nmi_plots = Array.new
90
+ iChrCount=0
91
+ isRequireArgs=false
92
+ vname = resource+"_nmi_list"
93
+ init_name = resource+"_nmi_init"
94
+
95
+ string.split(//).each do |c|
96
+ if c=='%' then
97
+ isRequireArgs=true
98
+ next
99
+ end
100
+ nmi_plots.push [x+iChrCount,y] if nmi_plots.index([x+iChrCount, y]).nil?
101
+ if !isRequireArgs then
102
+ @code_blocks.push sprintf("#{vname}[%d]=%s;", nmi_plots.index([x+iChrCount,y])*3+2, sprintf("%#x", c.bytes.to_a[0]-32))
103
+ iChrCount+=1
104
+ else
105
+ if c=='d' then
106
+ @code_blocks.push sprintf("#{vname}[%d]=0x10+%s;", nmi_plots.index([x+iChrCount,y])*3+2, args.shift)
107
+ iChrCount+=1
108
+ elsif c=='s' then
109
+ args.shift.split(//).each do |d|
110
+ nmi_plots.push [x+iChrCount,y] if nmi_plots.index([x+iChrCount, y]).nil?
111
+ @code_blocks.push sprintf("#{vname}[%d]=%s;", nmi_plots.index([x+iChrCount,y])*3+2, sprintf("%#x", d.bytes.to_a[0]-32))
112
+ iChrCount+=1
113
+ end
114
+ else
115
+ raise "method #show can't take any data except for %d or %s. Please make sure you are passing correct 4th parameter."
116
+ end
117
+ end
118
+ isRequireArgs=false
119
+ end
120
+
121
+ if nmi_plots.count>0 then
122
+ # declaration for show method
123
+ @global.push sprintf("static unsigned char #{vname}[%d*3];", nmi_plots.count)
124
+ @global.push sprintf("const unsigned char #{init_name}[%d*3]={", nmi_plots.count)
125
+ nmi_plots.each_with_index do |plot,i|
126
+ @global.push sprintf("MSB(NTADR(%d,%d)),LSB(NTADR(%d,%d)),0%s", plot[0],plot[1],plot[0],plot[1],i<(nmi_plots.count-1)?",":"")
127
+ end
128
+ @global.push "};"
129
+ @code_blocks.unshift "memcpy(#{vname},#{init_name},sizeof(#{init_name}));", sprintf("set_vram_update(%d,#{vname});", nmi_plots.count)
130
+ end
131
+
132
+ end
133
+ end
134
+
135
+ def wait(interval)
136
+ #@context.instance_exec do
137
+ # [
138
+ # "while(1){",
139
+ # "ppu_on_all();",
140
+ # "delay(#{interval});",
141
+ # "ppu_off();",
142
+ # "break;",
143
+ # "}"
144
+ # ].each {|p| @code_blocks.push p}
145
+ #end
146
+ @context.instance_exec {@code_blocks.push "delay(#{interval});"}
147
+ end
148
+
149
+ def goto(scene_name)
150
+ @context.instance_exec do
151
+ [
152
+ "ppu_off();",
153
+ "set_vram_update(0,0);", #clear vram condition for #show
154
+ "vram_adr(0x2000);", #clear nametable
155
+ "vram_fill(0,1024);", #clear nametable
156
+ "goto #{scene_name};"
157
+ ].each {|p| @code_blocks.push p}
158
+ end
159
+ end
160
+
161
+ def main_loop(rrb_source=nil)
162
+ rrb_source = File.open("#{@resource_name}.rrb").read if rrb_source.nil? || File.extname(rrb_source) == "#{@resource_name}.rrb"
163
+
164
+ # preprocess - for show method, all parameters after the 3rd parameter must be converted to String.
165
+ rrb_source = rrb_source.lines.map{|line|
166
+ if /^[\s\t]*show/ =~ line then
167
+ line.chomp.split(",").each_with_index.map{|data, i|
168
+ if i>2 then
169
+ '"' + data + '"'
170
+ else
171
+ data
172
+ end
173
+ }.join(",")
174
+ else
175
+ line.chomp
176
+ end
177
+ }.join("\n")
178
+
179
+ @context.instance_exec(@context,@resource_name) do |c, resource|
180
+ # preprocess
181
+ [
182
+ "ppu_on_all();",
183
+ "while(1){",
184
+ "ppu_waitnmi(); //wait for next TV frame",
185
+ ].each {|p| @code_blocks.push p}
186
+
187
+ # parse ruby source code and process them partially
188
+ p = Pxes::Cc65Transpiler.new(Ripper.sexp(rrb_source),c,resource)
189
+ #require 'pp'
190
+ #pp p
191
+ @code_blocks.push p.to_c
192
+
193
+ # postprocess
194
+ @code_blocks.push "}"
195
+ end
196
+
197
+ end
198
+
199
+ def inline(code)
200
+ @context.instance_exec {@code_blocks.push code}
201
+ end
202
+
203
+ #def sprite_set(x,y,tile_number,palette=0)
204
+ # @context.instance_exec {@code_blocks.push "oam_spr(#{x},#{y},0x"+tile_number.to_s(16)+",#{palette},0);//0x40 is tile number, i&3 is palette"}
205
+ #end
206
+
207
+ def sprite(resource)
208
+ @context.instance_exec {@code_blocks.push "sprite(&#{resource});"}
209
+ end
210
+
211
+ def screen(map, vars)
212
+ @context.instance_exec(@resource_name) do |resource|
213
+ current_char = nil
214
+ consective_count = 0
215
+ output = Array.new
216
+ # the last character "\b" is just a dummy to finialize loop process
217
+ (map.gsub(/(\r\n|\r|\n)/,"")+"\b").chars do |c|
218
+ if current_char != c then
219
+ if !current_char.nil? then
220
+ if vars.keys.include?(current_char.to_sym) then
221
+ output.push @pattern_table_index[vars[current_char.to_sym].to_sym] # user defined specific pattern
222
+ else
223
+ output.push 0 # blank pattern
224
+ end
225
+ # repeat to draw same pattern
226
+ output.push 1, consective_count if consective_count>0
227
+ end
228
+ current_char = c
229
+ consective_count = 0
230
+ else
231
+ consective_count += 1
232
+ end
233
+ end
234
+ @global.push "const unsigned char screen_#{resource}["+(output.count+3).to_s+"]={0x01,"+output.map{|p| sprintf "0x%.2x", p}.join(",")+",0x01,0x00};"
235
+ # "unrle_vram" means "decode Run Length Encoding and set them to vram".
236
+ @code_blocks.push "unrle_vram(screen_#{resource},0x2020);" # previously used 0x2000 but it doesn't display very first line of screen.
237
+ end
238
+ end
239
+
240
+ def paint(dest, palette)
241
+ @context.instance_exec(@resource_name) do |resource|
242
+ var_name="screen_#{resource}_color_#{@global.count}"
243
+ @global.push "const unsigned char #{var_name}[6]={0x01,0x"+Scene.const_get(palette.upcase).to_s(16)+",0x01,"+sprintf("0x%.2x",dest.end-dest.begin)+",0x01,0x00};"
244
+ @code_blocks.push "unrle_vram(#{var_name},0x"+(9152+dest.begin).to_s(16)+");" # 9152 is equivalent to 23C0
245
+ end
246
+ end
247
+
248
+ def sound(effect_name)
249
+ @context.instance_exec {@code_blocks.push "sfx_play(SFX_#{effect_name.upcase},0);"}
250
+ end
251
+
252
+
253
+ # vcall-ish methods
254
+ def rand
255
+ @context.instance_exec {@code_blocks.push "rand8()"}
256
+ end
257
+
258
+ alias_method :play_sound, :sound
259
+ alias_method :play_music, :play
260
+
261
+ # utility methods
262
+ def range(x_from, y_from, x_to, y_to)
263
+ x_to = x_from if x_to.nil?
264
+ y_to = y_from if y_to.nil?
265
+
266
+ plots = [x_from,y_from,x_to,y_to].map do |v|
267
+ if v.is_a?(String) && v.end_with?("px") then # x:0-255, y:0:239
268
+ v.to_i / 32
269
+ else
270
+ v.to_i / 4
271
+ end
272
+ end
273
+
274
+ plots[1]*8+plots[0]..plots[3]*8+plots[2]
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end