Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
You are not logged in - nap
CSDb User Forums


Forums > C64 Coding > Linking wizardry
2014-10-31 14:41
Bitbreaker

Registered: Oct 2002
Posts: 508
Linking wizardry

You wanted to have some linking wizardry, so here's how things were done in Comaland:

The central part:
-----------------

In $demofolder/link there's a Makefile. The example shows the process for doing one side. More imageX.d64 can of course be generated to build more sides, but i shortened things here to give at least a minimal chance to keep the overview. Some additional shellscript with sed wizardry was used to autogenerate a .bat for the windows whiners :-P
Targets like image1.d64 or vice1 can be used to build and fire up only a single side for standalone testing. Still, Alt-W is your best friend ;-)

export PATH := tools:../../link/tools/:$(PATH)
SHELL = /bin/bash
BITNAX = bitnax
D64WRITE = d64write
X64 = x64
MAKE_FLAGS = RELEASE=1

.PHONY: all toolchain vice vice1

all: toolchain image1.d64

toolchain:
        $(MAKE) -C ../../../tool/c6510
        $(MAKE) -C ../../../tool/acme/src
        $(MAKE) -C ../../../tool/dasm
        $(MAKE) -C ../../../tool/bitfire
        $(MAKE) -C ../../../tool/bitnax
        $(MAKE) -C ../../../tool/dreamass

        cp ../../../tool/c6510/c6510 tools/
        cp ../../../tool/acme/src/acme tools/
        cp ../../../tool/dasm/dasm tools/
        cp ../../../tool/dreamass/dreamass tools/
        cp ../../../tool/bitfire/d64write tools/
        cp ../../../tool/bitnax/bitnax tools/

vice: all
        $(X64) -pal -autostart "image1.d64:*" -truedrive -model c64c

vice1: toolchain image1.d64
        $(X64) -pal -autostart "image1.d64:*" -truedrive -model c64c

###############################################################

image1.d64: comaland.prg bootside1.prg tune1.prg rasterrot.prg explode.prg bitmap.prg bitmap_fadeout2.prg shadowscroll.prg shadow_fadeout.prg plotballs_fadein.prg plotballs.prg plotballs_fadeout.prg comalanddef.prg  escos.prg note.prg
        $(D64WRITE) -c $@ --side 1 -a 49 ../bitbreaker/dirart/side1.prg \
                                --boot comaland.prg \
                                -b bootside1.prg \
                                -b rasterrot.prg \
                                -b tune1.prg \
                                -b explode.prg \
                                -b bitmap1.prg \
                                -b bitmap2.prg \
                                -b bitmap3.prg \
                                -b bitmap4.prg \
                                -b bitmap5.prg \
                                -b bitmap_fadeout2.prg \
                                -b comalanddef1.prg \
                                -b comalanddef2.prg \
                                -b plotballs_fadein.prg \
                                -b plotballs.prg \
                                -b plotballs_fadeout.prg \
                                -b shadowscroll1.prg \
                                -b shadowscroll2.prg \
                                -b shadowscroll3.prg \
                                -b shadow_fadeout.prg \
                                -b escos1.prg \
                                -b escos2.prg \
                                -b escos3.prg \
                                -s "erotic note"

##################### SIDE 1 ##################################
note.prg: force_look
        cd ../Axis/note/; $(MAKE) $(MAKE_FLAGS)
        $(BITNAX) --bitfire --sfx 0x4000 -o "erotic note" ../Axis/note/note

comaland.prg: force_look
        cd ../bootloader/stage1/; $(MAKE) $(MAKE_FLAGS) link_exit=0x0100 SIDE=1
        $(BITNAX) --sfx 0x0900 -o $@ ../bootloader/stage1/stage1

bootside1.prg: force_look
        cd ../bootloader/stage2/; $(MAKE) $(MAKE_FLAGS) link_exit=0x2000 SIDE=1
        $(BITNAX) --bitfire -o $@ ../bootloader/stage2/stage2

tune1.prg: ../../Music/dEViLOCk/tune_side10x900.prg
        $(BITNAX) --bitfire -o $@ --load-addr 0xe000 $^

rasterrot.prg: force_look
        cd ../Axis/RasterRotate/; $(MAKE) $(MAKE_FLAGS) link_exit=8192
        $(BITNAX) --bitfire -o $@ ../Axis/RasterRotate/rasterrot

explode.prg: force_look
        cd ../Mirage/FontExplodeMultiplex; $(MAKE) $(MAKE_FLAGS) link_exit=0x300c
        $(BITNAX) --bitfire -o $@ ../Mirage/FontExplodeMultiplex/explode

bitmap.prg: force_look
        cd ../Lavazza/bitmapscroller/; $(MAKE) $(MAKE_FLAGS) link_exit=0x0400
        $(BITNAX) --bitfire -o $(basename $@)1.prg --cut-input-addr 0x3000 0xffff ../Lavazza/bitmapscroller/bitmap
        $(BITNAX) --bitfire -o $(basename $@)2.prg ../Lavazza/bitmapscroller/part2.prg
        $(BITNAX) --bitfire -o $(basename $@)3.prg ../Lavazza/bitmapscroller/part3.prg
        $(BITNAX) --bitfire -o $(basename $@)4.prg ../Lavazza/bitmapscroller/part4.prg
        $(BITNAX) --bitfire -o $(basename $@)5.prg ../Lavazza/bitmapscroller/part5.prg

bitmap_fadeout2.prg: force_look
        cd ../CRT/bitmap_fadeout2/; $(MAKE) $(MAKE_FLAGS) link_exit=0x6400
        $(BITNAX) --bitfire -o $@ ../CRT/bitmap_fadeout2/bitmap_fadeout2

comalanddef.prg: force_look
        cd ../Bob/bob_yazoo/; $(MAKE) $(MAKE_FLAGS) link_exit=0xf000;
        $(BITNAX) --bitfire -o $(basename $@)1.prg --cut-input-addr 0xbff8 0xffff --load-addr 0xe400 ../Bob/bob_yazoo/comalanddef
        $(BITNAX) --bitfire -o $(basename $@)2.prg --cut-input-addr 0x2000 0xbff8 ../Bob/bob_yazoo/comalanddef

plotballs_fadein.prg: force_look
        cd ../bitbreaker/plotball_fadein/; $(MAKE) $(MAKE_FLAGS) link_exit=0x4000;
        $(BITNAX) --bitfire -o $@ --load-addr 0x9b00 ../bitbreaker/plotball_fadein/plotball_fadein

plotballs.prg: force_look
        cd ../bitbreaker/plotballs/; $(MAKE) $(MAKE_FLAGS) link_exit=0x0400;
        $(BITNAX) --bitfire -o $@ ../bitbreaker/plotballs/plotballs

plotballs_fadeout.prg: force_look
        cd ../bitbreaker/plotball_fadeout/; $(MAKE) $(MAKE_FLAGS) link_exit=0x2000;
        $(BITNAX) --bitfire -o $@ ../bitbreaker/plotball_fadeout/plotball_fadeout

shadowscroll.prg: force_look
        cd ../CRT/shadowscroll; $(MAKE) $(MAKE_FLAGS) link_exit=0x0400
        $(BITNAX) --bitfire -o $(basename $@)1.prg --cut-input-addr 0xcff0 0xffff ../CRT/shadowscroll/shadowscroll
        $(BITNAX) --bitfire -o $(basename $@)2.prg --cut-input-addr 0x2000 0xbff8 ../CRT/shadowscroll/shadowscroll
        $(BITNAX) --bitfire -o $(basename $@)3.prg --cut-input-addr 0xbff8 0xcff0 ../CRT/shadowscroll/shadowscroll

shadow_fadeout.prg: force_look
        cd ../CRT/shadow_fadeout/; $(MAKE) $(MAKE_FLAGS) link_exit=0x2000
        $(BITNAX) --bitfire -o $@ ../CRT/shadow_fadeout/shadow_fadeout

escos.prg: force_look
        cd ../Swallow/escos_scroll; $(MAKE) $(MAKE_FLAGS) link_exit=0x0100
        $(BITNAX) --bitfire -o $(basename $@)1.prg --cut-input-addr 0xbff0 0xffff --load-addr 0xe400 ../Swallow/escos_scroll/escos
        $(BITNAX) --bitfire -o $(basename $@)2.prg --cut-input-addr 0x2000 0xbff0 ../Swallow/escos_scroll/escos
        cp ../Swallow/escos_scroll/low.prg $(basename $@)3.prg

###############################################################

clean:
        -rm *.prg *.d64
        -rm ../Axis/note/note
        -rm ../bootloader/stage1/stage1
        -rm ../bootloader/stage2/stage2
        #side1
        -rm ../Axis/RasterRotate/rasterrot
        -rm ../Lavazza/bitmapscroller/bitmap
        -rm ../CRT/bitmap_fadeout2/bitmap_fadeout2
        -rm ../CRT/shadowscroll/shadowscroll
        -rm ../CRT/shadow_fadeout/shadow_fadeout
        -rm ../bitbreaker/plotball_fadein/plotball_fadein
        -rm ../bitbreaker/plotballs/plotballs
        -rm ../bitbreaker/plotball_fadeout/plotball_fadeout
        -rm ../Mirage/FontExplodeMultiplex/explode
        -rm ../Swallow/escos_scroll/escos
        -rm ../Bob/bob_yazoo/comalanddef
        -rm "erotic note"

        #$(MAKE) -C ../../../tool/c6510 clean
        #$(MAKE) -C ../../../tool/acme/src clean
        #$(MAKE) -C ../../../tool/dasm clean
        #$(MAKE) -C ../../../tool/bitfire clean
        #$(MAKE) -C ../../../tool/bitnax clean
        #$(MAKE) -C ../../../tool/dreamass clean

force_look:
        @true

This is the main file to build the demo, there are blocks per side to be built. The part that writes all parts on disk builds up the dependencies to all the other targets.
Each part is build in a saparate subtarget in a separate dir with separate Makefile (tiny and can be copied over and adopted easily) also each part contains the entry point for the next upcoming part (link_exit). The compiled parts are split up and packed with bitnax. The chunks are choosen wisely so that they fit in during loading and not destroy/overwrite any code/data that is still in use. That is the nifty part about linking, stuffing as much as possible data into mem while still showing cool things on the screen. Also parts should split up wisely in size, so that they won't reach or go over the end of mem. If needed, the chunks can also be loaded to different locations by changing their load-address on packing.

The bootloader:
---------------

There's some code fragments for a stage1 and stage2 for the bootloader.

$demofolder/bootloader/stage1/main.asm
- contains code for installing the loader and code for loading stage 2
- contains persistent code that gives exit points for parts to be able to load things without own code (as it might be overwritten)
- contains music call and base irq that can take over the music while no part running. Also it keeps counting up the frame counter to be able to sync things.
- thus each side gets an installer/bootloader for safety reasons for free as well

$demofolder/bootloader/stage2/boot.asm
best placed @ $0100, but set the stackpointer to $ff before doing so.
The side specific stuff is happening here, like loading the music and starting it at the same frame for every try to keep teh demo in sync. Here it is also a good idea to set the music calls to different addresses if the music locations differes from side to side.

Synching:
---------

A sync point needs to be installed everywhere, where loading is not covered by a effect, as it is the only part that is not deterministic (still, longer depack times and such can also shift the sync). Also take care that the sync point waits a reasonable amount of frames longer than loading takes, to respect slower loading floppys. To find out the minimum time, you can freeze the machine after loading with a !byte $02 and read out the frame counter. It is usually a good idea to give about one second extra time.
Also, the demo is not synched to the music, but the music synched to the demo in the final step, or an recurring process. Why not offload some of the pain to the musicians :-P

The Parts:
----------

Each part has a own folder, like:
$demofolder/$codername/$partname

Each of those folders also contains a Makefile like:

ACME = acme
ACMEOPT = -v1 -f cbm

.PHONY: clean

all: bumble

bumble: afli.asm bumble_pic.prg
ifdef RELEASE
        ${ACME} ${ACMEOPT} -DRELEASE=1 -Dlink_exit=$(link_exit) -o $@ $<
else
        ${ACME} ${ACMEOPT} -o $@.prg $<
        ../../../../tool/bitnax/bitnax --sfx 0x9000 -o $@.prg $@.prg
endif

clean:
        -rm bumble bumble.prg


As you see, the target is build in a different way by using the RELEASE define. Also the parts can then make conditional assembly and either take precautions for a release or standalone version.
All this only works if the assembler accepts external defines. So far c6510, acme, dreamass and dasm do, but some only accept decimal values for that, liek dreamass, so take care on the link_exit define

A part loading another might then look like the following:

!ifdef RELEASE {
                ;reset frame counter
                lda #$00
                sta link_frame_count+0
                sta link_frame_count+1

                ;load + depack some part
                jsr link_loadnext_uncr

                ;load some part
                jsr link_loadnext_raw

!ifdef WAIT_FRAME_COUNTER {
                ;sync to frame counter
                +wait_frame_count $0200
} else {
                ;or wait for a trigger from your effect if you are sure it takes longer than loading
trig            lda #$00
                beq trig
}

                ;fadeout the effect
                jsr fadeout

                ;unlink irq to resident irq handler
                +link_player_irq

                ;depack last part loaded and start it
                +link_uncr_jmp link_exit
}


I'd add more details if that codebase64 would just be enabled for editing again :-) This is the suckless approach i guess, but of course not the most comfortable. Now throw in your bloated frameworks :-P
2014-10-31 20:29
HCL

Registered: Feb 2003
Posts: 728
Looks very sophisticated already :). Well, we do most stuff like this of course, but we don't build all binaries from the central makefile, but we rather copy each .prg file manually to the link-dir. ..which feels very stone-age really. Ruk/BD will probably change that to the next demo :), I'm too lazy for such stuff apparently :P.

..so if each one is using different assemblers and stuff it doesn't matter, it's just a .prg that is delivered to the link anyway.


I would like to add a mind-set when i start coding an effect or whatever. Every part that i code i *know* it is going to be linked once, so why don't give it a little effort right away. The last 5 demos or so (i really don't know) i have use the same kind of pattern when starting my parts, so it is something that i just copy from the last effect in a folder near by. Something like:

Start
	jmp S1
	jmp S2
S1
	; Do some precalc..
	; Expand some graphics..
	; Setup Irq
	rts
S2
	lda Exit
	bne *-3
	rts

Exit	.byte 1


Somewhere in the code, Exit will be set to 0, which simply means the part is done, we have faded down to black(?) and someone else can take over. Music is still running of course by some interrupt.

The effect should always be performed from within the (or some) interrupt. Only heaven knows when you want to do some loading, and you can not possibly guess where in the demo this part will be located and if loading is needed there, so let's just assume that all parts are loading parts.

To this code you then need a startup code that is probably lying after a basic sys-line.
	*= $0801
	.word BasicLineEnd
	.word 0
	.byte $9e ;sys
	.pet "2061"
	.byte 0
BasicLineEnd
	.word 0

	sei
	lda #$35
	sta 1

	jsr Start
	jsr Start+3

	inc $d020
	jmp *-3


Now this is only used for testing of course. When i want a binary for linking i leave out the basic-line and the code above (with IFDEFs). But i can still see how the part should be used from the linking file. The linking file would look something like..

	; setup loader and stuff..

	lda #MyPart
	jsr Load
	jsr Start   ; Run my part
	jsr Start+3 ; End my part

	; do other stuff..


Then shit always happen.. Perhaps Bob/Censor has the part after yours, so you need to pre-load as much as possible during your part. Simply find out where you have some unused memory and..

	; setup loader and stuff..

	lda #MyPart
	jsr Load
	jsr Start   ; Run my part
	lda #BobsPartPreload1
	jsr Load
	lda #BobsPartPreload2
	jsr Load
	jsr Start+3 ; End my part

	; do Bob's stuff..
	lda #BobsPartWhateverIsLeft
	jsr Load
	jsr BobsPart   ; Run BobsPart
	jsr BobsPart+3 ; End BobsPart


So.. this is what you have to deliver if you want to be a coder in BoozeDesign. That's all :). No magic, you probably have something similar yourselves. You need some structure in your code, and this is a little bit of how we do it..
2014-10-31 21:22
Slammer

Registered: Feb 2004
Posts: 416
Interesting thread here, good to hear your thougts and experiences.

What about things like timings? Do you have global counters or do you peek in the music player to know when to start an effect. And what about the takeover process. Anybody tried that the music sounds wierd because the interupt playing it is placed half a frame later in the next part, and do you have any strategies to avoid it?

Hcl: I have a strong urge to ask you if you don't feel crippled by having the line of control lying inside the interrupt. Controlling the effect flow seams so much easier to do under the interrupt (like your linking routine), but I see the point in the flexibility of being able to load everywhere and I guess you could use the wait routine for controlling the flow instead of waiting in special cases.

Edit: Ahh. Did first see the last text of first post now. It friday, hard week :-)
2014-10-31 21:43
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
Yep, a good way so far. But the problems always occur on the nitty gritty details.

Mostly the biggest concern on linking is the memory. For a big trackmo, you have to create and maintain a memory map for the whole demo. Not just a snapshot of a single part. More a 2D-memory map on a time line.

As HCL said, every part is/should be a loading part, otherwise you will suffer synching issues and black screen loading pauses. So every part is somehow executed in stages: loading, decrunching, initialization, runtime, shutdown. Every of these stages has a different memory footprint and layout. And succeeding parts should never overlap in memory. This would result in a crash, because part one runtime and shutdown will load, decrunch and initialize part 2, and so on.

I think everyone can imagine how annoying this is. Especially when the coders of the parts work on them and their memory layout changes 3 times a day. So normally the linking guy takes the first 3-4 hours every day to play sherlock holmes and fix all the sabotage that was done yesterday.

Then normally the time comes when the designers start to play dice with the running order and everything is totally fucked up and the work of the last 2 weeks is for the dustbin.

And until now I am just scratching at the surface of problems and sabotage techniques. Not covering: IRQ´s fucking up registers, broken stack-pointers, overwritten zp-vars and load-buffers, writing random values to $DD00 or $01, insert your experiences here, ...

Another problem on the fact, that everything should be a loading part is, that all effects running slower than 50 fps get really ugly in a trackmo. Because you have to split them up into parts that fit into 50 fps and schedule them into your IRQ´s. Writing task-managers, state-machines, whatever. If you then have to mix this up with IRQ heavy displays like sprite-multiplexers: HAVE FUN!
2014-10-31 21:46
raven
Account closed

Registered: Jan 2002
Posts: 137
I found it much easier to "peek" into the music player, for sync.
Its also easy to "debug" the sync this way for a single part - just fast-forward the music until you reach your part-entry point & then let the part run.

Also, once you tie your sync to the music player, you can never go out of sync, no matter what happens ;)

As for takeover process: if, for example, the current IRQ plays music at raster position X and the next IRQ plays it at X+200, you can gradually transition between them in steps.
That worked for me quite nicely.
2014-10-31 21:54
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
We always worked with counters for timing. So the coders have the timing under control. And a musician cant cause the demo to crash or hang.

For the problem of "sound glitches", thats how we call that effect, when a soundplayer-call jumps to a different rasterline. Moving the playback line in small steps (16 lines per frame is a good rule of thumb) is the way to go. To avoid bigger glitches (full frames of playback missing or done twice), always guard IRQ switches with VSync.
2014-10-31 21:56
raven
Account closed

Registered: Jan 2002
Posts: 137
What Axis said :)

Sadly, not every part can be a loading part.
One way of dealing with it, if you dont want a black-screen or injecting an additional transition-part, is to exit a part in steps.

For example, disable some of the part's functionality but leave its background graphics, while doing some quick loading, then finish the transition.

A part can also start in steps, like this:
1) Fade in some gfx (while loading)
2) Do some more loading
3) Start the effect

It gets much more complicated when two linked parts overlap in memory in such a way that you need to start juggling stuff around in memory while the part(s) are partially active...
2014-10-31 21:59
raven
Account closed

Registered: Jan 2002
Posts: 137
By the way, sometimes a lot can be learned by running a demo from a real drive & paying attention to load periods & whats going on the screen while they occur.

I used to do that a lot & still do, sometimes.
2014-10-31 22:19
Peiselulli

Registered: Oct 2006
Posts: 81
>>Because you have to split them up into parts that fit into 50 fps and schedule them into your IRQ´s

Normally I use nested interrupts for this kind of problems.
2014-10-31 22:32
Oswald

Registered: Apr 2002
Posts: 5094
suprised to hear some of you constantly change parts, even part order while linking.

never ever did that, and I would kill whoever wanted me to do any of that :)

also interesting to read about the 16 rasterlines music player shift, never had the idea to do that :) I just made sure frames are not skipped.

this thread remembers me when I could link the endpart for sixties revival live on the party for the first try. now that was fucking awesome.

no makefile shit. just a real c64 and AR and TASS. and time pressure :)
2014-10-31 22:35
Oswald

Registered: Apr 2002
Posts: 5094
Quote: >>Because you have to split them up into parts that fit into 50 fps and schedule them into your IRQ´s

Normally I use nested interrupts for this kind of problems.


yep..


irq:

...

lda effectalreadyrunning
bne yes

inc effetalreadyrunning
jsr effect
dec effetalreadyrunning

yes

cli
rti
2014-10-31 22:37
Peiselulli

Registered: Oct 2006
Posts: 81
I would put the "cli" before "jsr effect" ;-)
2014-10-31 22:54
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
Haha, AR and TASS. Good old days... No!

6-7 years ago we did a lot of last minute part shuffling. We didnt care about black screen loading and transitions to that time. So changing part order was just reordering the files copied to the disk and go!

Nowadays we try to avoid it. But sometimes you cant. When you realize that the whole stuff is not fitting on a disk-side. Or you have 2 parts by Bob succeeding. Without a single byte left to do transitions between. ;o)
Just kidding. In Comaland all coders rided the mem to the limit and beyond. In 50% of the parts, you cant even add a nop to avoid flickering rasters.
2014-10-31 22:57
Count Zero

Registered: Jan 2003
Posts: 1932
Truely refreshing to see people one usually meets completely drunk (on at least one side of the meeting parties) discuss this interesting topic obviously quite sober :)

"Linking" ... Something crackers have mastered a long time ago already of course *g* *ducks&runs*
2014-10-31 22:59
HCL

Registered: Feb 2003
Posts: 728
@Slammer: I guess i'm used to it.. I always use state-machines, lame code like:

	lda State
	cmp #1
	bcc DoStep0
	beq DoStep1
	..etc.


Jackasser hates me for this, but heck.. it worx. Often you just need 4 states for an effect.. something like: init and config, fadein, wait, fadeout. Then it loops. Then perhaps something that detects when the effect has been displayed 3 times and trigger exit.

@Axis: You describe hell! I think just as every part is a loading part, also every coder is a linker. You have to decide a few weeks before release in which order the parts should be. You have to know who is before you and perhaps who is after you. Even if your part is not 100% done, you probly have an idea of if the memory usage will increase or decrease. Of course late changes must be made sometimes, but not like we add another Bob/Censor-part 2 days before party..

What you said about effects running slower than 50 fps is of course true. Sometimes i make an exception from the golden rule that all parts are loading parts, especially if there are not much memory left anyway.. Like in the ChaosZoomer in Uncensored.

Oh, and we also use a global counter for music sync.
2014-11-01 00:26
JackAsser

Registered: Jun 2002
Posts: 2014
I've learned to love the way HCL link. He tought me well. Regarding complex state machines for part control, when your re-entrant IRQ code turns a bit too nasty I usually shuffle the code around into a co-routine which runs linear but jumps out and in at every "jsr yield", then I simply let the current raster code handle the CPU-jump. But usually it's just as simple as HCL put it.

For the next demo I'll try to force my group members to use some predefined ZP-pointer as current music location, that way the parts will always JSR to the same address and doesn't have to care where the musician and the link-script put the music routine.

Regarding timing and sync we always use a global frame counter that the music routine is responsible for updating, but that will change a bit we think due to being able to sync on effects in the music rather than the timing per se.
2014-11-01 00:33
raven
Account closed

Registered: Jan 2002
Posts: 137
There's nothing like coding an entire demo using TASS & some cart like AR and doing all the linking manually, using only a 1541 drive.

That is stamina.
Also, I'll never ever do that again, even for money :)
2014-11-01 00:39
CreaMD

Registered: Dec 2001
Posts: 3057
Quoting Bitbreaker
For the next demo I'll try to force my group members to use some predefined ZP-pointer as current music location, that way the parts will always JSR to the same address and doesn't have to care where the musician and the link-script put the music routine.


Neat idea!

-

As far as using music for timing is concerned. Since I once tried the approach of timing demo to music, (and failed :-), I always thought that next thing I will do is controlling demo through music by leaving points in music where coder can hook changes on. Never tried anything bigger than a simple demo on it, though.
2014-11-01 00:51
raven
Account closed

Registered: Jan 2002
Posts: 137
I also tend to use a simple state-machine for part control, but state changes are triggered by the music.
Basically, I like music sync to control everything.
2014-11-01 02:33
CreaMD

Registered: Dec 2001
Posts: 3057
Quote: I also tend to use a simple state-machine for part control, but state changes are triggered by the music.
Basically, I like music sync to control everything.


Insomnia!!! \o/
2014-11-01 09:26
MagerValp

Registered: Dec 2001
Posts: 1078
This is the best thread ever.

JackAsser, interesting that you bring up coroutines, I've been thinking about using them lately for a game project. Are you using any macros to pretty it up or is it just straight asm? Would you mind sharing a code snippet?
2014-11-01 11:09
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: This is the best thread ever.

JackAsser, interesting that you bring up coroutines, I've been thinking about using them lately for a game project. Are you using any macros to pretty it up or is it just straight asm? Would you mind sharing a code snippet?


No macros. I separate threads and co-routines in that my co-routines doesn't support stack-manipulaiton (i.e. JSR,PHA,PLA,TXS inside the co-routine is prohibited), where as my threads can handle a stack properly. The only difference between the two is how the co-operative scheduler works. For co-routines you don't have to split the stack, just save the regs and jump to the PC since the last call to yield. For threads the stack has to be split with some TXS/TSX magic, otherwise they're the same.

Here's my co-operative thread implementation that supports one thread at stack $80. It's easy to extend to support more threads and equally easy to reduce to co-routines by stripping out the stack management:

.proc irq
			pha
			txa
			pha
			tya
			pha
			lda $01
			pha
			lda #$35
			sta $01
			dec $d019
			jsr $1003

			lda active
			beq exitIRQ
			lda inHandler
			bne exitIRQ

			inc inHandler
			
			;Save main stack pointer
			tsx
			stx mainSP
			
			;Set thread stack pointer
			ldx threadSP
			txs
			
			; Restore thread CPU-state
			pla
			tay
			pla
			tax
			pla
			plp
			
			; Resume handler
			rts			
.endproc

mainSP:     .byte 0
threadSP:   .byte $80
handler:	.word mainloop-1 ; -1 for RTS-setup
inHandler: 	.byte 0
active:		.byte 0

.proc initThread
			tsx
			stx :+ +1
			
			; Enter thread stack
			ldx threadSP
			txs
			
			; Push exit thread handling when the thread perform RTS
			lda #>(exitThread-1)
			pha
			lda #<(exitThread-1)
			pha
			
			; Push return addy
			lda handler+1
			pha
			lda handler+0
			pha			
			
			; Set thread CPU-state
			lda #$00
			sta inHandler
			pha
			pha
			pha
			pha

			; Save thread stack
			tsx
			stx threadSP
			
			; Activate
			lda #1
			sta active

			:ldx #00
			txs
			rts
.endproc

.proc yield
			; Save thread CPU-state
			php
			pha
			txa
			pha
			tya
			pha
			
			; Save thread stack pointer
			tsx
			stx threadSP
			
			; Restore main stack pointer
			ldx mainSP
			txs
			dec inHandler
.endproc

.proc exitIRQ
			pla
			sta $01
			pla
			tay
			pla
			tax
			pla
			rti
.endproc

.proc exitThread
			; Reset thread stack
			lda #$80
			sta threadSP
			
			; Terminate
			lda #0
			sta active

			; Restore main stack pointer
			ldx mainSP
			txs

			jmp exitIRQ
.endproc

.proc joinThread
			lda active
			bne joinThread
			rts
.endproc
2014-11-01 11:41
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
Nice approach with the co-routines jacky. I think some macros would help to generalize this a bit more.

By the way, another nice technique to overcome loading-pauses is streaming. In Coma Light 13 there are some places where image data is not stored as "Koala-file", but in an interleaved reordered custom format. So that the fadein and the loading of the picture that gets faded in can be done in parallel. So the block that gets faded in was just loaded a few frames before.
2014-11-01 12:33
chatGPZ

Registered: Dec 2001
Posts: 11386
i am surprised how little i have to add ... =) maybe one thing: in artphosis we used a small memory resistant subroutine that parts call instead of the music player, that subroutine increments the global counter for timing stuff, and also peeks into the music player to set a "bassdrum" flag for use in parts... that way updating or even changing the music was fairly easy. oh, and i increment that timing counter at the same speed as the music "ticks", ie when music is "speed 5" then the counter would increase every 5th frame - then in parts you sync to counter values dividable by pattern length, and sync points magically fit perfectly on the beat. (also make sure to reset the counter when call music init, of course)
2014-11-01 12:47
HCL

Registered: Feb 2003
Posts: 728
That's ossom Jackasser, Axis too. I guess i'm more of a doer when it comes to stuff like that. But Groepaz's music handler i could rip right away! Very useful, and straight forward enough for me to grasp :).
2014-11-01 13:35
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
Updating the timer in the same "stepping" as the musicplayer is a very good idea. This will prevent us from that nasty +-1 frame finetuning which can take hours on a big demo.
2014-11-01 13:44
chatGPZ

Registered: Dec 2001
Posts: 11386
yes. and you can start it with appropriate number of frames delay for perfect match in emulamor while developing :) (and 16 bit timer will not wrap around for at least one disk side, which may help too)
2014-11-01 13:49
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: That's ossom Jackasser, Axis too. I guess i'm more of a doer when it comes to stuff like that. But Groepaz's music handler i could rip right away! Very useful, and straight forward enough for me to grasp :).

Doesn't really make me more productive, but it's fun to do some actual computer science every now and then. :)
2014-11-01 13:50
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
Hehe, yes. Never thought about that wrapping problem. I like to do fast paced demos. So 21 Minutes should be enough for everyone! ;o)
2014-11-01 14:43
MagerValp

Registered: Dec 2001
Posts: 1078
Thanks for sharing JackAsser, I have a thread scheduler in my head that I've yet to find any use for, and this is great inspiration.
2014-11-01 14:52
Oswald

Registered: Apr 2002
Posts: 5094
I do that music stuff as gpz, except the music tick. suprised its new stuff to some of you :)

also always keep a music player irq resident "tempirq".
2014-11-01 17:07
Stone

Registered: Oct 2006
Posts: 172
In RGB, the "main" tune plays through all 3 disk sides. This was accomplished by splitting the tune into several parts with a small segment at the end which loops until the next part is ready or the disk is turned. The music player signals when the loop point has been reached and the next tune part can start seamlessly.

Regarding linking: I built my parts with 2 entry points in the same PRG. One cold start (can be run from basic) and a warm start which is called from the demo system. Saves me the trouble of building two versions with different build parameters. Pantaloon doesn't really approve of this though, since it fills up the disk :P

Interesting to read about JackAsser's threading/coroutine system. I'm using coroutines myself (with macros) and I originally got the idea when we were porting the NBA Hangtime arcade game to SNES. There, each object/actor had its own private stack (with local variables). My system (first used in Save Truckstop Alaska/We Are All Connected) is more limited, with just A/X/Y/PC saved. No nested subroutines or stack shenanigans allowed. It uses macros for things like yielding and sleeping and plain rts at the end of a "task".
2014-11-01 21:36
Radiant

Registered: Sep 2004
Posts: 639
Wow, lots of nice reading! I don't have much to add, other than ld65 can be a very powerful tool when linking. In our framework I've added support for splitting up the linker config files, so you can have definitions that are common for all parts of the demo in one or more files, and part specific definitions in others. Most of the work around linking is actually handled by ld65 and Make (and a small, resident "chaining" routine which loads the parts in order).

We also use the linker config for declaring timing symbols (based on a global frame counter). This means you can pretty much move parts around as you want, provided they're well behaved - and provided they don't use the same memory areas...

To solve that in the standard framework setup there are two different memory configs; one using $2000-$dfff and another using $e000-$fff8 for part specific data. The idea is that the smaller parts work as intermediate loader parts while the larger ones are proper effects. The framework starts loading the next part the moment the current part returns from the main thread. Before returning the parts can use all memory between $2000 and $fff8 as they see fit, but when the next part is loading they have to stay away from that memory area.

I've thought about automatically (or at least semi-) splitting up parts in smaller chunks and loading them incrementally, but haven't written any code to do so yet. Also it would be nice to be able to build the demo to run from a specific part from a disk image without running it in standalone PRG mode, if the part for example loads custom data from disk and needs the resident loader to work.

Really like JackAsser's threading approach. *steals* :-)
2014-11-01 23:00
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: Thanks for sharing JackAsser, I have a thread scheduler in my head that I've yet to find any use for, and this is great inspiration.

Any time your re-entrant state machine code in the IRQ becomes too messy you have a use for it in that your code turns from state machine type to linear flowimg code. Adjust the yield-function too your needs, f.e. pausr one frame or even yield to a particular raster line, or to the next music beat etc.
2014-11-02 09:14
doynax
Account closed

Registered: Oct 2004
Posts: 212
How do you guys keep the weird-and-wonderful dependency chains of the build manageable?

Admittedly I've not been working large collaborative demo projects (though I imagine that can only make things worse) but I always seem to end up massaging code and data back and forth in fifteen ways through tools before the build is completed. The result being that I tend spend a significant amount of time tracking down dependency bugs in the build script and forcing full rebuilds at the slightest provocation.

Surely there's got to be a better tool than make out there though, especially the recursive variety. Unfortunately my last attempt to find one mostly turned up slew of tools aimed to ease cross-platform compilation. Personally I eventually ended up kludging together a build engine driven by some Lua scripts but let's just that it's still far from being the ideal tool..
2014-11-02 09:35
Axis/Oxyron
Account closed

Registered: Apr 2007
Posts: 91
A thing I always thought about, but never implemented yet, is adding a "dev" or "debug" build target to our single parts and full demo build. To check for common sources of error.

In the single parts this target can invoke code before init and after shutdown. To mark the critical zp-adresses and memory areas (music, loader, framework, ...) with a pattern. And check that pattern to not be broken after shutdown. Otherwise an error-message will be shown. Basically like C-compilers do on debug builds with malloc.

Another common problem is, that timing marks that work perfect one day to cover the whole loading time on every device dont fit anymore a few days later. this can be caused by parts growing, or even by other parts earlier on the disk that move the succeeding parts on different tracks and sectors. I can tell you, that phenomenom has bitten us uncountable times, always in the worst moments. And mostly during development (Vice or Ultimate), everything is fine. But then someone does a test on a slow floppy drive and the whole timing is fucked. The "debug" build could check if all timing marks still have enough safety-margin (e.g. 50 frames). And if not, exit the demo with an error-message.
2014-11-02 10:33
Radiant

Registered: Sep 2004
Posts: 639
Quoting doynax
Surely there's got to be a better tool than make out there though, especially the recursive variety. Unfortunately my last attempt to find one mostly turned up slew of tools aimed to ease cross-platform compilation. Personally I eventually ended up kludging together a build engine driven by some Lua scripts but let's just that it's still far from being the ideal tool..

IMO there are currently no better tools out there than Make. Tracking dependencies is a tough problem in general, and like with most tough problems there are no easy solutions. I usually end up hardcoding the dependencies, falling back on full rebuilds occasionally... But that's mostly due to stupid laziness.
2014-11-02 10:59
MagerValp

Registered: Dec 2001
Posts: 1078
Quoting Stein
I'm using coroutines myself (with macros)


Sounds awesome, please share!
2014-11-02 11:10
doynax
Account closed

Registered: Oct 2004
Posts: 212
Quoting radiantx
IMO there are currently no better tools out there than Make. Tracking dependencies is a tough problem in general, and like with most tough problems there are no easy solutions. I usually end up hardcoding the dependencies, falling back on full rebuilds occasionally... But that's mostly due to stupid laziness.
I suppose that's true but an improved tool certainly couldn't hurt in getting the rules right.

Why am I forced to put up with a wonky declarative script instead of a sane imperative scripting language as god intended? Why not spare a few cycles to verify the precise file information against a database and check up on that the expected artifacts really did get generated instead of relying on relative time-stamps which always seem to get out-of-sync? Why not make the command executable an implicit dependency of the rule? And so on, and so forth.

</rant>

Quoting Axis/Oxyron
Another common problem is, that timing marks that work perfect one day to cover the whole loading time on every device dont fit anymore a few days later. this can be caused by parts growing, or even by other parts earlier on the disk that move the succeeding parts on different tracks and sectors. I can tell you, that phenomenom has bitten us uncountable times, always in the worst moments. And mostly during development (Vice or Ultimate), everything is fine. But then someone does a test on a slow floppy drive and the whole timing is fucked. The "debug" build could check if all timing marks still have enough safety-margin (e.g. 50 frames). And if not, exit the demo with an error-message.
Good point. I don't suppose some kindly soul might get around to adding a VICE/Ultimate option to control the rate of rotation and help test the extreme values.
2014-11-02 11:14
chatGPZ

Registered: Dec 2001
Posts: 11386
you could file a feature request for custom floppy RPM in VICE.... :)
2014-11-02 11:24
Radiant

Registered: Sep 2004
Posts: 639
Quoting doynax
Why am I forced to put up with a wonky declarative script instead of a sane imperative scripting language as god intended? Why not spare a few cycles to verify the precise file information against a database and check up on that the expected artifacts really did get generated instead of relying on relative time-stamps which always seem to get out-of-sync? Why not make the command executable an implicit dependency of the rule? And so on, and so forth.

I definitely think the future of build scripting lies in an imperative approach. Waiting for someone to do it right! :-)
2014-11-02 11:36
chatGPZ

Registered: Dec 2001
Posts: 11386
kindof of funny how every development related discussion at some point ends up in:

- writing a build system
- writing a file system
- writing an operating system

=)
2014-11-02 11:36
Pantaloon

Registered: Aug 2003
Posts: 124
i did write my own script instead of using make. not sure why i did it really but im pretty happy with how little i have to do to get things done now. I have a few different functions like:

addSourceFile <filename> <dependencies> <nameondisk> <safetyMargin>

addBinaryFile <filename> <dependencies> <nameondisk> <safetyMargin>

I also have the option to produce makefiles and visual studio solutions directly from this file.

Example:


    startupFile     = "Source/Fx/Intro/Startup.s"
    d64image        = "Image\RGB (A).d64"    
    d64diskname     = "      R-G-B     "
    d64diskid       = " (F) "	
    interleave      = 5

    addSourceFile "Source/DemoSystem.s", "",  "", -1

    addSourceFile "Source/Fx/Intro/Startup.s", "Source/DemoSystem.s", "00", -1

    addSourceFile "Source/Fx/Intro/FadeOutChars.s", "Source/DemoSystem.s", "FC", -1
    addSourceFile "Source/Fx/Intro/BasicScreenFade.s", "Source/DemoSystem.s", "BF", -1

    addSourceFile "Source/Fx/Intro/X2014.s", "Source/DemoSystem.s", "X2", -1

    addSourceFile "Source/Fx/Intro/Metaballs.s", "Source/DemoSystem.s", "MB", -1
    addSourceFile "Source/Fx/Intro/MetaballsScreens.s", "Source/DemoSystem.s", "MS", -1
    addSourceFile "Source/Fx/Intro/Rgb.s", "Source/DemoSystem.s", "RG", -1

    addSourceFile "Source/Fx/Ext/Lars/Trees/Trees1.s", "Source/Fx/Ext/Lars/Trees/Trees.s", "L1", -1
    addSourceFile "Source/Fx/Ext/Lars/Trees/Trees2.s", "Source/Fx/Ext/Lars/Trees/Trees.s", "L2", -1 
    addSourceFile "Source/Fx/Ext/Lars/Trees/Trees3.s", "Source/Fx/Ext/Lars/Trees/Trees.s", "L3", -1
    addSourceFile "Source/Fx/Ext/Lars/Trees/Trees4.s", "Source/Fx/Ext/Lars/Trees/Trees.s", "L4", 64

2014-11-02 13:43
MagerValp

Registered: Dec 2001
Posts: 1078
Quote: you could file a feature request for custom floppy RPM in VICE.... :)

And simulating random read errors would also be useful, though it would be good to have a genuine unreliable drive to model it after.
2014-11-02 13:50
MagerValp

Registered: Dec 2001
Posts: 1078
As for build systems, declarative is the only sane approach. Makefile syntax is kind of crappy and annoying, but this paper taught me a lot about writing Makefiles for larger projects: Recursive Make Considered Harmful
2014-11-02 14:42
Perplex

Registered: Feb 2009
Posts: 255
Quoting radiantx
I definitely think the future of build scripting lies in an imperative approach. Waiting for someone to do it right! :-)


Ruby + Rake => Bliss.
2014-11-02 14:48
Stone

Registered: Oct 2006
Posts: 172
Quote: Quoting Stein
I'm using coroutines myself (with macros)


Sounds awesome, please share!


I shared the source files here (ca65):

https://dl.dropboxusercontent.com/u/59267720/c64/asm/task.s
https://dl.dropboxusercontent.com/u/59267720/c64/asm/task.inc

Initialize task system with:
jsr task_init

Run all tasks for 1 tick, typically from your irq handler:
jsr task_handler

Example usage (Save Truckstop Alaska "game over" screen):

https://www.youtube.com/watch?v=jGAsiHY1VCs#t=975

.include "gameoverscreen.inc"
.include "screen.inc"
.include "task.inc"
.include "mymacros.s"
.include "globals.inc"

line1_y = 10
line2_y = line1_y + 2
line3_y = line1_y + 4
line4_y = line1_y + 6

jumpman_sprite=global_bank+$2000
jumpman_dead = 56

gameoverscreen_run:
	jsr screen_clear
	lda #0
	jsr screen_fillcolorram
	
	ldx #line1_y
	ldy #0
	jsr screen_set_cursor_pos
	ldax #gameover1
	jsr screen_print_centered
	
	ldx #line2_y
	ldy #0
	jsr screen_set_cursor_pos
	ldax #gameover2
	jsr screen_print_centered
	
	ldx #line3_y
	ldy #0
	jsr screen_set_cursor_pos
	ldax #gameover3
	jsr screen_print_centered
	
	ldx #line4_y
	ldy #0
	jsr screen_set_cursor_pos
	ldax #gameover4
	jsr screen_print_centered

	TASK_ADD gameoverscreen_task, #0, #$80
	TASK_WAITFOR gameoverscreen_task
	rts

gameoverscreen_task:
	ldx #156
	ldy #180
	stx $d000
	sty $d001
	lda #$02
	sta $d025
	lda #$04
	sta $d026
	lda #1
	sta $d027
	lda #0
	sta $d01b
	sta $d010
	lda #1
	sta $d015
	sta $d017
	sta $d01c
	sta $d01d
	lda #(jumpman_sprite&$3fff/64)+jumpman_dead
	sta global_screen+$03f8
	
	lda #((global_screen&$3fff)>>6)|((global_font&$3fff)>>10)
	sta $d018
	lda #((global_bank>>14)^3)
	sta $dd00
	lda #$c8
	sta $d016
	lda #$1b
	sta $d011
	
	TASK_ADD sprite_task, #0, #0
	
	TASK_ADD flashline_task, #line1_y, #80
	TASK_SLEEP #100

	TASK_ADD flashline_task, #line2_y, #80
	TASK_SLEEP #100
	
	TASK_ADD flashline_task, #line3_y, #80
	TASK_SLEEP #100

	TASK_ADD flashline_task, #line4_y, #80
	TASK_SLEEP #200
	
	lda #6
	jsr global_tune_setfade
	TASK_SLEEP #200
		
	TASK_KILLTASK sprite_task

	lda #0
	sta $d015
	sta $d017
	sta $d01d
	lda #$0b
	sta $d011
	rts

sprite_task:
@loop:
	TASK_SLEEP #4
	lda global_screen+$03f8
	eor #1
	sta global_screen+$03f8
	jmp @loop
		
flashline_task:
@loop:
	tax
	lda flashcolors,y
	bmi @done
	iny
	sty @y
	sta @val
	lda screen_lo,x
	sta @col+0
	lda screen_hi,x
	and #3
	ora #$d8
	sta @col+1
	ldy #$27
:
@val=*+1
	lda #0
@col=*+1
	sta $dead,y
	dey
	bpl :-	
@y=*+1
	ldy #0
	txa
	TASK_SLEEP #7
	jmp @loop
@done:
	rts
	
gameover1:
	.byte "THE TRUCKSTOP ALASKA IS IN RUINS", 0
gameover2:
	.byte "DATASTORM WILL BE NO MORE",0
gameover3:
	.byte "WE WILL CHERISH THE MEMORIES", 0
gameover4:
	.byte "GAME OVER",0
	
flashcolors:
	.byte $00, $0b, $0c, $0f
	.repeat 16
	.byte $01
	.endrepeat
	.byte $0f, $0c, $0b, $00
	.byte $ff		
2014-11-02 18:22
Pex Mahoney Tufvesson

Registered: Sep 2003
Posts: 52
I end my parts with:
pla
pla
rts
...adjust the numer of pla's with how deep in subroutine trouble you are...! ;)
---
Have a noise night!
http://mahoney.c64.org
2014-11-02 18:23
Radiant

Registered: Sep 2004
Posts: 639
Quoting MagerValp
As for build systems, declarative is the only sane approach. Makefile syntax is kind of crappy and annoying, but this paper taught me a lot about writing Makefiles for larger projects: Recursive Make Considered Harmful

I disagree that declarative is the only sane approach; I think it is a sane approach, but that attacking the problem from the other end could be quite viable as well. (Of course a solution with its own set of drawbacks.)

Nice article! I hadn't read it before, but I recognize a lot of the problems with a recursive build. I've solved most of the issues by minimizing dependency overlap between components, which I do think has its own merits, but I'll definitely consider switching to single Makefile builds in the future.
2014-11-03 07:28
Peacemaker

Registered: Sep 2004
Posts: 275
my idea was to take the music routine to handle the synch. talked about it with groepaz for the next demo, if it ever will come =) . having another channel / pattern in the music player (goatracker has the stereo player which would be a good way to use it) with commands that will flag "next part". or trigger small effects that needs synch with the music, without testing to get the right time position by frame :)
2014-11-03 08:26
Mr. SID

Registered: Jan 2003
Posts: 424
As far as build systems go, I've used SCons on large projects in the past, and I have plans to move my C64 projects over to that as well.
2014-11-03 08:45
Bitbreaker

Registered: Oct 2002
Posts: 508
Quoting doynax
How do you guys keep the weird-and-wonderful dependency chains of the build manageable?


Good question. as in my example, there's a few things that break dependencies yet. Like changing entry points for parts, it is most likely not really resolvable, except when taking the main Makefile as dependency. Also, all packing is done on every build over and over again, what is of course also unnecessary, and most likely the most time consuming part. New project, new improvements :-) So far there were other things that stole more time.

Also, some other nice approaches here, using an extra track in goattracker is an option, but will however eat memory. That is why we decided to link as much as possible statically when needed, instead of holding various things in memory throughout the whole demo. That is also, why i added some not so often used functions to bitfire as includes, like a turn disk. No need to waste bytes all time, but only in the turn disk part.

The thing about calling parts via jsr is also an option, but also requires a kind of resident script to manage all calls. Also the stack should only be fucked to that extend to still hold the return address. Sometimes hard, either due to sabotage, or due to using the stack. Stack restoring gayness is the least thing i'd want to have :-)
2014-11-03 08:55
Peacemaker

Registered: Sep 2004
Posts: 275
indeed, will eat some bytes, if you use an extra channel for triggering, but i can be worth the extra bytes.. depends on what exactly you are doing. if you trigger small little effects, it can more than helpfull, or if you want routines that are not only time synched to the musik, but frequency and / or filter etc will also take part of the effect. but for "normal" time synching only, an extra channel is not needed.
2014-11-03 09:38
spider-j

Registered: Oct 2004
Posts: 498
Quoting Peacemaker
my idea was to take the music routine to handle the synch.

I'm working on sth. similar. The question is how do you handle loading times when music syncs 'everything'? I don't have that much experience with loading, but as far as I heard it's never 100% exactly the same.
So the concept I'm working on for MYDs first trackmo is a mixture: music syncs a single part until part fade-out, then loader gets 'priority' - musics jumps into a filler-loop (utilizing SID-Wizard subtune jump feature) - when loading is ready, music gets the 'control' again.
GT stereo is also a nice idea for music sync. But I guess I'll just patch the SW player using commands I don't use anyway in the music for sync stuff. As the source is well documented this seems a quite 'easy' task. To get more rastertime I'll just disable every player feature that isn't used by the music.
2014-11-03 10:07
Bitbreaker

Registered: Oct 2002
Posts: 508
Spider-j:

as stated: hand over irq to a handler that will not be overwritten and that counts up a frame counter (16 bit) on each call and keeps playing music. then instruct the loader to load the next part and depack it. After that hang the machine and seize all interrupts to find out how long loading takes (the frame counter will tell you) Let's say it is now at $100, now add some reasonable overhead to it to cope for slower drives, $140 should be a good value.
Now remove the code that hangs the machine, but add a barrier that waits for the framecounter to be at or above $140 frames. Then, and only then start the next part. Thus the next part. You can use that method also on other occasions, by having a special music call that will always bump your framecounter.

That is what you call instead of jsr $1003 instead:

link_music_play
                inc link_frame_count+0
                bne +
                inc link_frame_count+1
+
                jmp link_music + 3



A macro like this will help you set synching points:

                !macro wait_frame_count .cnt {
-
                        lda link_frame_count+0
                        cmp #<.cnt
                        lda link_frame_count+1
                        sbc #>.cnt
                        bcc -
                }


i prefer resetting the framecounter after each time i want to reach a new sync point, in order to have relative times. This way you don't have to adopt all later synch points if something in the beginning changes.
2014-11-03 10:30
Hein

Registered: Apr 2004
Posts: 954
Quote: my idea was to take the music routine to handle the synch. talked about it with groepaz for the next demo, if it ever will come =) . having another channel / pattern in the music player (goatracker has the stereo player which would be a good way to use it) with commands that will flag "next part". or trigger small effects that needs synch with the music, without testing to get the right time position by frame :)

GT mono allready has a pattern command that does that.

from the readme:
Command DXY: Set mastervolume to Y, if X is $0. If X is not $0, value XY is
             copied to the timing mark location, which is playeraddress+$3F.
2014-11-03 12:07
Hate Bush

Registered: Jul 2002
Posts: 465
Quote: GT mono allready has a pattern command that does that.

from the readme:
Command DXY: Set mastervolume to Y, if X is $0. If X is not $0, value XY is
             copied to the timing mark location, which is playeraddress+$3F.


confirmed. we used that feature for synchronising vectors in Sweet Infection and the intro in Biba 3.
2014-11-03 15:31
Peacemaker

Registered: Sep 2004
Posts: 275
hein: thanks.. that was a usefull info.
2014-11-03 15:57
MagerValp

Registered: Dec 2001
Posts: 1078
@stein: thanks for that, I really like that you can pass an argument in A, and that it has priority handling.
2014-11-03 18:41
Dane

Registered: May 2002
Posts: 423
Great thread, apart from the fact that HCL now forced me to learn how to code in an xdev environment. :(
2014-11-03 20:48
Magnar

Registered: Aug 2009
Posts: 61
What Stein wrote about the looping of music and using commands in the patterns to indicate when it ends and either A) loop again or B) Continues to next song/patterns is neat.

It is a bit similar to the idea I've had for a very long time of a tackmo demo where you could pause a part by say press space at a certain time and then the part would loop forever until you again pressed space for continue with the demo. This would allow a trackmo to also become a "press space" demo for those parts you would like to watch for a longer time.

An example is when a coder spent half a year on a effect, and it goes by in a demo in less than 20 seconds... By having a "press space to enter space-mode", this effect could be enjoyed forever :) Until you exit by pressing space again.

In my prototype for this, I have used the same technique as Stein talked about, but in a different way. My way of doing this is by waiting for a effect command in the song to show the "press space message", the song will thereafter play another pattern, and at the end of that pattern, if space hasn't been pressed, the demo will just continue. If spacebar is pressed, the song will play until end of the song pattern and then switch to song nr 2 (or 3, or 4 etc) in the subsongs to play a sequence of a completely new song that loops for that effect.

Again, in the end of that sub-song, you get the possibility to exit by pressing space again. When that happens, and space is pressed, the song will have a transition in the last pattern so it fits back to the main melody in song nr.1, and then continue the demo and the song from that Point in song 1 as if spacebar never been pressed. And the trackmo continues.

I could share my prototype for this if any is interested. :)

Cheers
2014-11-03 21:24
lft

Registered: Jul 2007
Posts: 369
This most excellent thread seems like a good place to remind people about Spindle (Spindle 1.0).

It provides a 2D map of page usage over time, like Axis asked for. It splits parts into chunks and loads them incrementally, like radiantx suggested. It is faster than Krill's loader, thanks to on-the-fly GCR decoding. Because it's open source, it can easily be adapted to any special requirements.

Of course, it's not the final answer to everything. It has been designed primarily for design-time flexibility (turning linking into a creative activity, like film editing) and loading speed, at the cost of a relatively high memory footprint (1 kB) and sub-optimal compression (due to out-of-order decrunching). But it's there if you need it, or if you just want some inspiration.
2014-11-04 19:32
Pex Mahoney Tufvesson

Registered: Sep 2003
Posts: 52
Spindle seems nice. Next time I do a trackmo, I'll use it for sure! But that might be next X, so there's plenty of time for anyone else to do it before me!

Oh, and I'm all for single makefiles. And I love in-details assembly code listings. Or "I'll show mine if you'll show yours", one of my favorite games all-time! ;)
---
Have a noise night!
http://mahoney.c64.org
2014-11-07 14:46
Glasnost
Account closed

Registered: Aug 2011
Posts: 26
Spindle looks very promising, but is it compatible with the variations in c64 drives? I mean, the GCR decoding on the fly is pure art, and with icu-peaking, spindle has dream speed. But what happens with 280 and 320 rpm drives? As far as i know, single drives could be this extreme, or?

Best regards
/Glasnost
2014-11-07 15:52
lft

Registered: Jul 2007
Posts: 369
Spindle relies on the same timing assumptions as Krill's loader.

Motor speed tolerance is discussed in this forum thread. See e.g. my post from 2012-10-24. Krill replies that no trouble has been reported with his 130-cycle loop, but I seem to remember that he changed it to 128 later.
2014-11-07 21:45
Glasnost
Account closed

Registered: Aug 2011
Posts: 26
Ok thx
2014-11-19 08:54
Mr. SID

Registered: Jan 2003
Posts: 424
Last night I've ditched my Makefile and used the DKjr project as a test case for building with SCons.
The result is a much leaner build script that correctly handles dependencies not just for includes but also incbin data, is easier to understand and maintain and something I can properly share between different projects without headaches.
2014-11-21 16:17
doynax
Account closed

Registered: Oct 2004
Posts: 212
This thread has prompted me to finally try to clean-up and prepare my attempt at an imperative build tool for release, after 5-odd years of incremental fiddling and personal use.

I'd like to take this opportunity to call on the community for a couple of guinea pigs to help test and critique the tool before locking down the design for a public release (or a public burning of the source out of shame.)

It's basically a build engine tacked on to a Lua interpreter, with some assorted features to help track implicit dependencies and the like.

Anyone dissatisfied with make and wanting to try something new? Then feel free to drop me a PM and we'll see whether you share my taste for minimalism, aversion to shell-scripting and desire for rapid iteration. I promise there's no obligation to try it, just take a quick look and see whether it might be something for you and your project.

Disclaimer: Windows/POSIX only. Software may be bug-ridden.
RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Advanced
Users Online
LordCrass
pcollins/Quantum
Guests online: 83
Top Demos
1 Next Level  (9.7)
2 13:37  (9.7)
3 Mojo  (9.7)
4 Coma Light 13  (9.6)
5 Edge of Disgrace  (9.6)
6 What Is The Matrix 2  (9.6)
7 The Demo Coder  (9.6)
8 Uncensored  (9.6)
9 Comaland 100%  (9.6)
10 Wonderland XIV  (9.6)
Top onefile Demos
1 No Listen  (9.6)
2 Layers  (9.6)
3 Cubic Dream  (9.6)
4 Party Elk 2  (9.6)
5 Copper Booze  (9.6)
6 Dawnfall V1.1  (9.5)
7 Rainbow Connection  (9.5)
8 Onscreen 5k  (9.5)
9 Morph  (9.5)
10 Libertongo  (9.5)
Top Groups
1 Performers  (9.3)
2 Booze Design  (9.3)
3 Oxyron  (9.3)
4 Triad  (9.3)
5 Censor Design  (9.3)
Top Logo Graphicians
1 t0m3000  (10)
2 Sander  (9.8)
3 Mermaid  (9.5)
4 Facet  (9.4)
5 Shine  (9.4)

Home - Disclaimer
Copyright © No Name 2001-2024
Page generated in: 0.124 sec.