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
 
... 57 posts hidden. Click here to view all posts....
 
Previous - 1 | 2 | 3 | 4 | 5 | 6 | 7 - Next
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: 84
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 Diskmag Editors
1 Magic  (9.8)
2 hedning  (9.6)
3 Jazzcat  (9.5)
4 Elwix  (9.1)
5 Remix  (9.1)

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