Slammer
Registered: Feb 2004 Posts: 416 |
(EDIT: Use a wide screen to view this)
Some people have asked, 'do we really need the script language?', and the answer is 'no, but it can make life easier'. Some might think its only made for doing speed code and while you have to do code generators for the the final product anyway, so why bother? Well, I use it for rapid prototyping of speed code, but also for also for other cases, and here is one of them - generating (big) effect tables.
I find that when you are editing an effect table (control parameters for your demopart), or another kind of data structure, it's nice to have the values placed close together on the same line when you edit them, e.g.:
effect1: .byte Param1, Param2, Param3, Param4
effect2: .byte Param1, Param2, Param3, Param4
..
Why this is so, will become obvious when we later look at a practical example. However, the most effective way of accessing the parameters is to have each byte in a separate table so you can just write lda param1,x instead of having to calculate the start of the effect parameters, adding the offset etc.
Param1: .byte effect1, effect2,
Param2: .byte effect1, effect2, ...
Param3: .byte effect1, effect2, ...
Param4: .byte effect1, effect2, ...
To overcome this we write a structure for editing the parameters. To see this in action, here is a real life example. First we define a structure and some constants to be used when defining the effects.
.struct EffectParam { duration,
xstart1, xstart2, xadd1, xadd2, xscale1, xscale2, xcountAnd1, xcountEor1,
ystart1, ystart2, yadd1, yadd2, yscale1, yscale2, ycountAnd1, ycountEor1
}
.const NONE = 8
.const bar = 80
.enum {Scale1=0, Scale2=1, Scale3=2, Scale4=3, Scale0 = 4 }
And then the effect table. It's timed after the music and is rather large (You have to play the music through 3 times to see all effects), but luckily we can now write comments so we will know where we are relative to the music and what the effect does. (The comments might be deprecated, I just copy pasted this from actual code):
.var effectList = List().add(
// Song variation 1: Free first moves
EffectParam(/*Duration*/8*bar, /*X*/0,2,3,4,Scale2,Scale3,%1111,%000, /*Y*/4,6,1,5,Scale2,Scale2, %111,%000),
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,1,7,Scale2,Scale2,%1111,%000, /*Y*/NONE,NONE,4,4,Scale2,Scale2, %111,%000),
// Song Variation 2
EffectParam(/*Duration*/4.5*bar, /*X*/0,0,4,4,Scale2,Scale2,%111,%000, /*Y*/2,2,4,4,Scale2,Scale2, %111,%000), // Circle
EffectParam(/*Duration*/3.5*bar, /*X*/0,0,4,4,Scale3,Scale3,%111,%000, /*Y*/6,6,4,4,Scale3,Scale3, %111,%000), // Circle inv
EffectParam(/*Duration*/4.5*bar, /*X*/0,0,4,4,Scale2,Scale2,%111,%000, /*Y*/2,2,4,4,Scale2,Scale2, %111,%000), // Circle
EffectParam(/*Duration*/3.5*bar, /*X*/0,0,4,4,Scale3,Scale3,%111,%000, /*Y*/6,6,4,4,Scale3,Scale3, %111,%000), // Circle inv
//--------------------
// Song Intro
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,1,5,Scale3,Scale3,%1111,%000, /*Y*/NONE,NONE,3,7,Scale3,Scale3, %111,%000),
// Song variation 1
EffectParam(/*Duration*/3*bar, /*X*/NONE,NONE,3,7,Scale3,Scale3, %111,%000, /*Y*/NONE,NONE,5,6,Scale2,Scale4,%111,%000), // Call:
EffectParam(/*Duration*/1*bar, /*X*/ 7, 7,10,10,Scale1,Scale1,%111,%000, /*Y*/ 0, 0,2,0,Scale2,Scale2,%111,%000), //
EffectParam(/*Duration*/3*bar, /*X*/NONE,NONE,3,7,Scale2,Scale4, %111,%000, /*Y*/NONE,NONE,5,6,Scale2,Scale2,%111,%000), // Answer:
EffectParam(/*Duration*/1*bar, /*X*/ 3, 3,10,10,Scale1,Scale1,%111,%000, /*Y*/ 0, 0,2,0,Scale3,Scale3,%111,%000), //
EffectParam(/*Duration*/3*bar, /*X*/NONE,NONE,3,7,Scale2,Scale2,%111,%000, /*Y*/NONE,NONE,5,6,Scale2,Scale2,%111,%000), // Call:
EffectParam(/*Duration*/1*bar, /*X*/ 7, 7,5,10,Scale1,Scale1,%111,%000, /*Y*/ 0, 0,2,0,Scale2,Scale4,%111,%000), //
EffectParam(/*Duration*/3*bar, /*X*/NONE,NONE,3,7,Scale2,Scale2,%111,%000, /*Y*/NONE,NONE,5,6,Scale2,Scale2,%111,%000), // Answer:
EffectParam(/*Duration*/1*bar, /*X*/ 0, 0,2,0,Scale2,Scale4,%111,%000, /*Y*/ 3, 3,10,10,Scale1,Scale1,%111,%000), //
// Song Variation 2
EffectParam(/*Duration*/4.5*bar, /*X*/0,0,4,4,Scale3,Scale3,%111,%000, /*Y*/2,2,4,4,Scale3,Scale3, %111,%000), // Circle
EffectParam(/*Duration*/3.5*bar, /*X*/0,0,4,4,Scale4,Scale4,%111,%000, /*Y*/6,6,4,4,Scale4,Scale4, %111,%000), // Circle inv
EffectParam(/*Duration*/4.5*bar, /*X*/0,0,4,4,Scale3,Scale3,%111,%000, /*Y*/2,2,4,4,Scale3,Scale3, %111,%000), // Circle
EffectParam(/*Duration*/3.5*bar, /*X*/0,0,4,4,Scale4,Scale4,%111,%000, /*Y*/6,6,4,4,Scale4,Scale4, %111,%000), // Circle inv
//---------- Funny moves-------
// Intro
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,7,2,Scale1,Scale2,%1111,%000, /*Y*/NONE,NONE,5,2,Scale1,Scale1, %011,%100), // + on y
// Song variation 1
EffectParam(/*Duration*/8*bar, /*X*/0,2,3,6,Scale2,Scale3,%111,%010, /*Y*/1,3,3,6,Scale2,Scale3, %111,%010), // Weird bump on both x and y
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,3,7,Scale2,Scale0, %111,%000, /*Y*/NONE,NONE,5,6,Scale2,Scale0,%111,%000), // Single sine
// Song variation 2
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,5,2,Scale2,Scale2, %101,%000, /*Y*/NONE,NONE,3,1,Scale2,Scale2,%111,%010), // Crazy push double sine
EffectParam(/*Duration*/4.5*bar, /*X*/0,0,4,4,Scale2,Scale2,%111,%000, /*Y*/2,2,4,4,Scale2,Scale2, %111,%000), // Circle
EffectParam(/*Duration*/3.5*bar, /*X*/0,0,4,4,Scale3,Scale3,%111,%000, /*Y*/6,6,4,4,Scale3,Scale3, %111,%000), // Circle inv
//-------------------
// Intro
EffectParam(/*Duration*/8*bar, /*X*/NONE,NONE,1,5,Scale3,Scale3,%1111,%000, /*Y*/NONE,NONE,3,7,Scale3,Scale3, %111,%000)
).lock()
Don't panic if you don't understand the meaning of all the parameters, its not important. Instead notice that we can now easily comment the data and divide it into sections. Its also easier to edit - copy/pasting or moving a set of parameters from one place to another suddenly becomes easy.
Now we have to store the parameters. But we also want to process the data a little before we store them. The duration can be more than 256 frames so we have to use two bytes, and the start parameters is only 4 bit wide so we can pack two of them into one byte with a lille function. Don't mind the details, just note that you can write your data in an easy understandable way and then process them to more optimal forms before storing them in memory. In our case it looks like this:
.function toStartByte(startPos1, startPos2) {
.var res1 = startPos1==NONE ? 1 : [startPos1<<1]
.var res2 = startPos2==NONE ? 1 : [startPos2<<1]
.return res1|[res2<<4]
}
.label noOfEffects = effectList.size()
durrationLoTab: .fill effectList.size(), <-[effectList.get(i).duration]
durrationHiTab: .fill effectList.size(), >-[effectList.get(i).duration]
xaddSin1Tab: .fill effectList.size(), effectList.get(i).xadd1
xaddSin2Tab: .fill effectList.size(), effectList.get(i).xadd2
xsinStartTab: .fill effectList.size(), toStartByte(effectList.get(i).xstart1,effectList.get(i).xstart2)
yaddSin1Tab: .fill effectList.size(), effectList.get(i).yadd1
yaddSin2Tab: .fill effectList.size(), effectList.get(i).yadd2
ysinStartTab: .fill effectList.size(), toStartByte(effectList.get(i).ystart1,effectList.get(i).ystart2)
xcountEorTab: .fill effectList.size(), effectList.get(i).xcountEor1
xcountAndTab: .fill effectList.size(), effectList.get(i).xcountAnd1
ycountEorTab: .fill effectList.size(), effectList.get(i).ycountEor1
ycountAndTab: .fill effectList.size(), effectList.get(i).ycountAnd1
xscale1Tab: .fill effectList.size(), effectList.get(i).xscale1 + >scale100Tab
xscale2Tab: .fill effectList.size(), effectList.get(i).xscale2 + >scale100Tab
yscale1Tab: .fill effectList.size(), effectList.get(i).yscale1 + >scale100Tab
yscale2Tab: .fill effectList.size(), effectList.get(i).yscale2 + >scale100Tab
Finally notice that the scale tab parameters (last four tables) are added with the hi byte of the first scale table (scale100Tab). The scripting is dependant on the code (scale100Tab) and the code is dependant on the effect table (The labels of the parameter tabs and the noOfEffect label). This two way dependancy makes it awkward to separate the generation of the effect table in another file, using another language and a makefile. In any case, I find it nice to have it right besides the rest of the code, without having to do additional setup.
So what we have done is:
1. Defined the effect table in the script language in an more readable and editable format
2. Transformed it in to a more machine friendly format automatically, instead of manually.
Without the ability to do this, I doubt I would have had the patience to set up effects for three runs through the music, and I wouldn't have the overview of the effect table that I have, if It was defined in the standard old fashion form. So clearly you can do a large effect table without the script language, but its pretty handy to have it available. |