The Source-Tile Manipulator
As I mentioned before, the source-tile is a small, 16x16 bitmap that I use as a seed image in many of my warp effects. In this example we'll take a look at a raw source-tile, and introduce a couple of new command-string options along the way. First, set up for the example: ensure that initlist = smplsrce_initlist and drawloop = drawframe_hl at the top of ol_demo2.s, then run the code.
You should see a raw, unfiltered view of a 16x16 tile, upon which a red blob is moving around. Although this doesn't look especially exciting at the moment, it does get better when used in conjunction with a nice warp or so! For now, let's have a look at the object definition and see what's going on; the sourcetile in this example is generated by sourcetile0.moo:
; ; sourcetile0.moo = a MacrOObject that ; defines a source tile. st0: .dc.s 0 ;Prev .dc.s 0 ;Next .dc.s ($0c030080|sourcetile) .dc.s 0 ;Address of parameter block if not localSo there are 12 secondary data longs, 3 variables, and the external code indicated by sourcetile is run once per update.
.dc.s 0 ;Address of ranges table, if not local .dc.s 0 ;this'll be where the command string is, if not local .dc.s 0 ;prototype to use .dc.s 0 ;no secondary dataEverything is local, and notice that there is no OLR prototype specified. If the OLR prototype is set to 0, then this object will not generate an OLR object to be added to the current OLR list. In effect, this is a non-displaying object. However, the space where the OLR object would have been generated is still available for use in the command string, and the first vector of that space is still passed over to the external routine when it is run. In this example we use it to bung the externl routine a few params.
.dc.s st0_end-st0 ;length .dc.s 0 ;init routine .dc.s $0 ;clock-mode .dc.s 0 .dc.s 0,0,0,0 ; local paramspace .dc.s 0 ;Phase relative to current .dc.s $2743 ;Phase offset .dc.s $0c100 ;Speed .dc.s 1 ;Mode (Sawtooth) .dc.s 0 ;Phase relative to current .dc.s $2743 ;Phase offset .dc.s $0f100 ;Speed .dc.s 2 ;Mode (Sine) .dc.s 0 ;Phase relative to current .dc.s $2743 ;Phase offset .dc.s $1c100 ;Speed .dc.s 2 ;Mode (Sine)Three unremarkable waveform variables...
; ranges .dc.s $20 .dc.s $80 .dc.s $f0 .dc.s $20 .dc.s 0 .dc.s $3fff0000 .dc.s $4ffff .dc.s $10 .dc.s $04ff0000 .dc.s $01ff0000,$1ff0000,0,0,0,0,0A local ranges table, with a bunch of gubbins left over from previous object definitions in it... only entries 3, 4 and 6 are used here...
; secondary data .dc.s 2,$00,thingy_masks,tile_img .dc.s $51f05a00,0,spot_mask,$7ffffff .dc.s $10808000,0,full_mask,$1f00000Now this section is the meat of the object. The sourcetile generator works in the following way:
This example uses two masks, spot_mask and full_mask. These are pretty much what you'd expect - spot_mask is a mask of a small spot, and full_mask is a mask with every bit set. Have a mess around - try changing full_mask to llama_mask and observe the results. Mess with the target colours and the blend rates, and see what happens.
This is all well and groovy, but how does the external routine get hold of the layer information? To see that, take a gander at the command string:
; command .ascii "_a=a" .ascii "$_e=b" .ascii "_d=c" ; .ascii "C[46]!#_c=_g" .ascii "A[43]=_f<" .ascii "B[43]=_f>:" .align.v st0_end:Remember that I said that even though this object doesn't generate an OLR object, the OLR object build space is still available? That's how the params arrive at the external routine. Whatever is in slots a...d will be present in r0...r3 on the external routine when it starts up. So, "_a=a" is obvious, setting up the first slot with the number of layers, so that will arrive in r0. But what about "$_e=b"?
Well, if you'll notice, the layer information starts at the second vector of secondary data space - a location that would normally be referred to as _e. The statement "_e=b" would place the contents of _e into b, whereas "$_e=b" places the address of _e into b. The "$" operator means "the address of", and is used here to pass in the address of the start of the layer information to the external routine, where it appears in r1.
The next statement, "_d=c", passes in the sourcetile address, tile_img, as the third param to the external routine, where it arrives in r2.
Ignore the commented line for now, and just notice the last two statements - these just use the A and B waves to generate the XY offset in _f, which is the offset word for the first layer. These two waves are what's making the spot move around on the tile.
Okay, now uncomment the commented line, and re-assemble. You should now see that Layer 1's mask is changing according to a regular sequence. By altering the speed and type of wave C, you can alter the way in which the sequence changes. This can be useful in any object that uses a sequence of frames for animation - successive sprites for a 2D walking character, for example, or successive polyline lists for a vector object.
To see how this works, check out the first vector of secondary data again:
.dc.s 2,$00,thingy_masks,tile_imgNote the reference to thingy_masks. Examining ol_demo2.s to see how this is defined, you'll see:
thingy_masks: .dc.l spot_mask,ring1_mask,ring2_mask,ring3_mask,llama_mask- a list of all the mask definitions that the first layer cycles through. Now, looking at the relevant portion of the command string, it should become clear what is going on. "C[46]!" is quite straightforward - evaluate wave C, scale to (limits(4),limits(6)), which in this case means to between 0 and $4ffff; then take the integer part by losing the low 16-bits, which yields a value between 0 and 4. The next part of the statement, "#_c", is the new bit: it means "use the current value as an index into the array of longwords pointed to by the contents of _c, and return the value stashed there". Since _c is set to thingy_masks, an address corresponding to one of the masks in that list is read out, and deposited into _g, which is the mask address of layer 1.
Try looking at the init_list for this example in ol_demo2.s. You'll find the list at smplsrce_initlist. Try changing the reference to source_tile0 to source_tile2, and reassemble to see the sourcetile that will be used in the next example. It's very similar to the simple example we just did, except that it uses more layers and munges the colours a bit! You may care to load up sourcetile2.moo to check out the definition. Then, when you're ready, proceed to the next example, where the sourcetile is going to get Warped!