| |
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 |
|
... 57 posts hidden. Click here to view all posts.... |
| |
Peiselulli
Registered: Oct 2006 Posts: 81 |
I would put the "cli" before "jsr effect" ;-) |
| |
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. |
| |
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* |
| |
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. |
| |
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. |
| |
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 :) |
| |
CreaMD
Registered: Dec 2001 Posts: 3057 |
Quoting BitbreakerFor 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. |
| |
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. |
| |
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/ |
| |
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? |
Previous - 1 | 2 | 3 | 4 | 5 | 6 | 7 - Next |