burn 0.1.3 → 0.2.0

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