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.
- data/README.md +56 -569
- data/lib/burn.rb +5 -3
- data/lib/burn/cli.rb +160 -199
- data/lib/burn/config.rb +4 -0
- data/lib/burn/config/app.rb +15 -0
- data/lib/burn/config/config_base.rb +16 -0
- data/lib/burn/config/loader.rb +23 -0
- data/lib/burn/config/server.rb +11 -0
- data/lib/burn/fuel.rb +11 -0
- data/lib/burn/{dsl → fuel}/dsl_base.rb +1 -1
- data/lib/burn/fuel/rom/channel_stream.rb +106 -0
- data/lib/burn/fuel/rom/declare.rb +43 -0
- data/lib/burn/fuel/rom/music.rb +37 -0
- data/lib/burn/fuel/rom/note.rb +126 -0
- data/lib/burn/fuel/rom/scene.rb +279 -0
- data/lib/burn/fuel/rom/sound.rb +31 -0
- data/lib/burn/fuel/rom/sound_effect.rb +92 -0
- data/lib/burn/fuel/telnet/declare.rb +26 -0
- data/lib/burn/fuel/telnet/scene.rb +112 -0
- data/lib/burn/generator.rb +10 -3
- data/lib/burn/generator/rom/assembly_music.rb +49 -0
- data/lib/burn/generator/rom/assembly_sound_effect.rb +35 -0
- data/lib/burn/generator/rom/c_source.rb +55 -0
- data/lib/burn/generator/{template → rom/template}/mus_template.s +0 -0
- data/lib/burn/generator/{template → rom/template}/template.c +0 -0
- data/lib/burn/generator/rom_builder.rb +67 -0
- data/lib/burn/generator/telnet/jit_compiler.rb +41 -0
- data/lib/burn/generator/telnet/screen.rb +86 -0
- data/lib/burn/generator/telnet/sprite.rb +16 -0
- data/lib/burn/generator/telnet/user_input.rb +25 -0
- data/lib/burn/generator/telnet_vm.rb +50 -0
- data/lib/burn/pxes.rb +3 -0
- data/lib/burn/{util/pxes.rb → pxes/cc65_transpiler.rb} +8 -14
- data/lib/burn/pxes/cruby_transpiler.rb +189 -0
- data/lib/burn/pxes/transpiler_base.rb +14 -0
- data/lib/burn/server.rb +2 -0
- data/lib/burn/server/rom.rb +24 -0
- data/lib/burn/server/telnet.rb +120 -0
- data/lib/burn/util.rb +1 -2
- data/lib/burn/util/logo.rb +68 -0
- data/lib/burn/util/os.rb +1 -1
- data/lib/burn/version.rb +1 -1
- metadata +53 -19
- data/lib/burn/builder.rb +0 -65
- data/lib/burn/dsl.rb +0 -8
- data/lib/burn/dsl/channel_stream.rb +0 -105
- data/lib/burn/dsl/declare.rb +0 -42
- data/lib/burn/dsl/music.rb +0 -36
- data/lib/burn/dsl/note.rb +0 -125
- data/lib/burn/dsl/scene.rb +0 -278
- data/lib/burn/dsl/sound.rb +0 -30
- data/lib/burn/dsl/sound_effect.rb +0 -91
- data/lib/burn/generator/assembly_music.rb +0 -47
- data/lib/burn/generator/assembly_sound_effect.rb +0 -33
- data/lib/burn/generator/c_source.rb +0 -54
- 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
|
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'
|
@@ -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
|