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: 498
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
 
... 57 posts hidden. Click here to view all posts....
 
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: 243
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: 419
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: 498
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: 243
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: 443
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: 498
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: 933
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: 451
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: 243
hein: thanks.. that was a usefull info.
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
swasti
Freeze/Blazon
Krill/Plush
t0m3000/ibex-crew
E$G/hOKUtO fOrcE
blitzed
Guests online: 395
Top Demos
1 Next Level  (9.8)
2 Mojo  (9.7)
3 Coma Light 13  (9.7)
4 Edge of Disgrace  (9.6)
5 No Bounds  (9.6)
6 Comaland 100%  (9.6)
7 Uncensored  (9.6)
8 The Ghost  (9.6)
9 Wonderland XIV  (9.6)
10 Bromance  (9.6)
Top onefile Demos
1 Party Elk 2  (9.7)
2 Cubic Dream  (9.6)
3 Copper Booze  (9.5)
4 Rainbow Connection  (9.5)
5 TRSAC, Gabber & Pebe..  (9.5)
6 Onscreen 5k  (9.5)
7 Dawnfall V1.1  (9.5)
8 Quadrants  (9.5)
9 Daah, Those Acid Pil..  (9.5)
10 Birth of a Flower  (9.5)
Top Groups
1 Booze Design  (9.3)
2 Nostalgia  (9.3)
3 Oxyron  (9.3)
4 Censor Design  (9.3)
5 Crest  (9.3)
Top Graphicians
1 Sulevi  (10)
2 Mirage  (9.8)
3 Lobo  (9.7)
4 Mikael  (9.7)
5 Archmage  (9.7)

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