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: 500
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-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: 3034
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: 1055
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: 1989
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

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: 11108
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: 716
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

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: 11108
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: 1989
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. :)
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
Apollyon/ALD
Freeze/Blazon
Linus/MSL
MightyAxle
Fred/Channel 4
Smiffy71/Feline Desi..
Guests online: 130
Top Demos
1 Next Level  (9.8)
2 Mojo  (9.7)
3 Coma Light 13  (9.7)
4 Edge of Disgrace  (9.6)
5 Comaland 100%  (9.6)
6 No Bounds  (9.6)
7 Uncensored  (9.6)
8 The Ghost  (9.6)
9 Wonderland XIV  (9.6)
10 Bromance  (9.6)
Top onefile Demos
1 It's More Fun to Com..  (9.8)
2 Party Elk 2  (9.7)
3 Cubic Dream  (9.6)
4 Copper Booze  (9.5)
5 Rainbow Connection  (9.5)
6 TRSAC, Gabber & Pebe..  (9.5)
7 Onscreen 5k  (9.5)
8 Wafer Demo  (9.5)
9 Dawnfall V1.1  (9.5)
10 Quadrants  (9.5)
Top Groups
1 Oxyron  (9.3)
2 Nostalgia  (9.3)
3 Booze Design  (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.081 sec.