From b9e3cb89bfd45b0635d93e6316ab87ee4cf0a376 Mon Sep 17 00:00:00 2001 From: FeralChild64 Date: Sun, 8 Sep 2019 22:17:49 +0200 Subject: [PATCH] new routine binning tool - modified dynamic knapsack solving algorithm --- .gitignore | 3 - Makefile | 197 +++--- bin/{REEADME.txt => README.txt} | 0 bin/basic_generic.rom | Bin 8192 -> 8192 bytes bin/basic_mega65.rom | Bin 8192 -> 8192 bytes bin/basic_ultimate64.rom | Bin 8192 -> 8192 bytes bin/kernal_generic.rom | Bin 8192 -> 8192 bytes bin/kernal_mega65.rom | Bin 8192 -> 8192 bytes bin/kernal_ultimate64.rom | Bin 8192 -> 8192 bytes src/build_segment.cc | 983 +++++++++++++++++++++++++++ src/compress_text.c | 2 +- src/{ => outdated}/basic005.c | 0 src/{ => outdated}/prepare_startup.c | 0 src/{ => outdated}/preprocess.c | 0 14 files changed, 1084 insertions(+), 101 deletions(-) rename bin/{REEADME.txt => README.txt} (100%) create mode 100644 src/build_segment.cc rename src/{ => outdated}/basic005.c (100%) rename src/{ => outdated}/prepare_startup.c (100%) rename src/{ => outdated}/preprocess.c (100%) diff --git a/.gitignore b/.gitignore index ffd89384..d9f921e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ # Compilation related files -temp.s -temp.sym - assembler/KickAss* build/* diff --git a/Makefile b/Makefile index 5080e7ed..66226086 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,41 @@ # Source files -SRC_COMMON = $(wildcard c64/aliases/*.s) -SRC_BASIC_COMMON = $(SRC_COMMON) $(wildcard c64/basic/*.s) -SRC_KERNAL_COMMON = $(SRC_COMMON) $(wildcard c64/kernal/*.s c64/kernal/jumptable/*.s c64/kernal/iec/*.s c64/kernal/interrupts/*.s) +SRCDIR_COMMON = c64/aliases +SRCDIR_BASIC_COMMON = $(SRCDIR_COMMON) c64/basic +SRCDIR_KERNAL_COMMON = $(SRCDIR_COMMON) c64/kernal c64/kernal/jumptable c64/kernal/iec c64/kernal/interrupts -SRC_BASIC_generic = $(SRC_BASIC_COMMON) $(wildcard c64/basic/,target_generic/*.s) -SRC_BASIC_mega65 = $(SRC_BASIC_COMMON) $(wildcard c64/basic/,target_mega65/*.s) -SRC_BASIC_ultimate64 = $(SRC_BASIC_COMMON) $(wildcard c64/basic/,target_ultimate64/*.s) +SRCDIR_BASIC_generic = $(SRCDIR_BASIC_COMMON) c64/basic/,target_generic +SRCDIR_BASIC_mega65 = $(SRCDIR_BASIC_COMMON) c64/basic/,target_mega65 +SRCDIR_BASIC_ultimate64 = $(SRCDIR_BASIC_COMMON) c64/basic/,target_ultimate64 -SRC_KERNAL_generic = $(SRC_KERNAL_COMMON) $(wildcard c64/kernal/,target_generic/*.s) -SRC_KERNAL_mega65 = $(SRC_KERNAL_COMMON) $(wildcard c64/kernal/,target_mega65/*.s) -SRC_KERNAL_ultimate64 = $(SRC_KERNAL_COMMON) $(wildcard c64/kernal/,target_ultimate64/*.s) +SRCDIR_KERNAL_generic = $(SRCDIR_KERNAL_COMMON) c64/kernal/,target_generic +SRCDIR_KERNAL_mega65 = $(SRCDIR_KERNAL_COMMON) c64/kernal/,target_mega65 +SRCDIR_KERNAL_ultimate64 = $(SRCDIR_KERNAL_COMMON) c64/kernal/,target_ultimate64 + +SRC_TOOLS = $(wildcard src/tools/*.c,src/tools/*.cc) # List of tools -TOOL_COLLECT_DATA = build/tools/collect_data -TOOL_COMPRESS_TEXT = build/tools/compress_text -TOOL_PNGPREPARE = build/tools/pngprepare -TOOL_PREPROCESS = build/tools/preprocess -TOOL_SIMILARITY = build/tools/similarity +TOOL_COLLECT_DATA = build/tools/collect_data +TOOL_COMPRESS_TEXT = build/tools/compress_text +TOOL_PNGPREPARE = build/tools/pngprepare +TOOL_BUILD_SEGMENT = build/tools/build_segment +TOOL_SIMILARITY = build/tools/similarity +TOOL_ASSEMBLER = assembler/KickAss.jar -TOOLS_LIST = $(TOOL_COLLECT_DATA) $(TOOL_COMPRESS_TEXT) $(TOOL_PNGPREPARE) $(TOOL_PREPROCESS) $(TOOL_SIMILARITY) +TOOLS_LIST = $(pathsubst src/tools/%,build/tools/%,$(basename $(SRC_TOOLS))) + +# List of targets + +STD_TARGET_LIST = build/kernal_generic.rom build/basic_generic.rom \ + build/kernal_mega65.rom build/basic_mega65.rom \ + build/kernal_ultimate64.rom build/basic_ultimate64.rom \ + build/chargen.rom + +EXT_TARGET_LIST = build/newc65.rom + +REL_TARGET_LIST = $(pathsubst build/%,bin/%, $(STD_TARGET_LIST)) # GIT commit @@ -31,126 +45,115 @@ GIT_COMMIT:= $(shell git log -1 --pretty='%h' | tr '[:lower:]' '[:upper:]') .PHONY: all clean updatebin -all: build/chargen build/newkern_generic build/newbasic_generic build/newc65 build/newkern_ultimate64 build/newbasic_ultimate64 +all: $(STD_TARGET_LIST) $(EXT_TARGET_LIST) clean: @rm -rf build - @rm -f temp.s - @rm -f temp.sym -updatebin: build/chargen build/newbasic_generic build/newkern_generic build/newbasic_mega65 build/newkern_mega65 build/newbasic_ultimate64 build/newkern_ultimate64 - cp build/chargen bin/chargen.rom - cp build/newbasic_generic bin/basic_generic.rom - cp build/newkern_generic bin/kernal_generic.rom - cp build/newbasic_mega65 bin/basic_mega65.rom - cp build/newkern_mega65 bin/kernal_mega65.rom - cp build/newbasic_ultimate64 bin/basic_ultimate64.rom - cp build/newkern_ultimate64 bin/kernal_ultimate64.rom +updatebin: $(STD_TARGET_LIST) + cp build/chargen.rom bin/chargen.rom + cp build/basic_generic.rom bin/basic_generic.rom + cp build/kernal_generic.rom bin/kernal_generic.rom + cp build/basic_mega65.rom bin/basic_mega65.rom + cp build/kernal_mega65.rom bin/kernal_mega65.rom + cp build/basic_ultimate64.rom bin/basic_ultimate64.rom + cp build/kernal_ultimate64.rom bin/kernal_ultimate64.rom # Rules - tools -$(TOOL_COLLECT_DATA): src/collect_data.c Makefile +$(TOOL_PNGPREPARE): src/pngprepare.c Makefile @mkdir -p build/tools - gcc -Wall -o $(TOOL_COLLECT_DATA) src/collect_data.c + $(CC) -O2 -Wall -I/usr/local/include -L/usr/local/lib -o $@ $< -lpng $(TOOL_COMPRESS_TEXT): src/compress_text.c Makefile @mkdir -p build/tools - gcc -g -Wall -o $(TOOL_COMPRESS_TEXT) src/compress_text.c -lm + $(CC) -O2 -Wall -I/usr/local/include -L/usr/local/lib -o $@ $< -lm -$(TOOL_PNGPREPARE): src/pngprepare.c Makefile +build/tools/%: src/%.c Makefile @mkdir -p build/tools - $(CC) $(COPT) -I/usr/local/include -L/usr/local/lib -o $(TOOL_PNGPREPARE) src/pngprepare.c -lpng + $(CC) -O2 -Wall -o $@ $< -$(TOOL_PREPROCESS): src/preprocess.c Makefile +build/tools/%: src/%.cc Makefile @mkdir -p build/tools - gcc -g -Wall -o $(TOOL_PREPROCESS) src/preprocess.c - -$(TOOL_SIMILARITY): src/similarity.c Makefile - @mkdir -p build/tools - gcc -g -Wall -o $(TOOL_SIMILARITY) src/similarity.c + $(CXX) -O2 -Wall -o $@ $< # Rules - CHARGEN -build/chargen: $(TOOL_PNGPREPARE) assets/8x8font.png - $(TOOL_PNGPREPARE) charrom assets/8x8font.png build/chargen +build/chargen.rom: $(TOOL_PNGPREPARE) assets/8x8font.png + $(TOOL_PNGPREPARE) charrom assets/8x8font.png build/chargen.rom -# Dependencies +# Dependencies - BASIC and KERNAL -build/basic_generic/OUT.BIN: $(TOOLS_LIST) $(SRC_BASIC_generic) build/basic/packed_messages.s -build/kernal_generic/OUT.BIN: $(TOOLS_LIST) $(SRC_KERNAL_generic) -build/newrom_generic: build/basic_generic/OUT.BIN build/kernal_generic/OUT.BIN -build/newkern_generic: build/newrom_generic Makefile -build/newbasic_generic: build/newrom_generic Makefile +build/target_generic/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_generic) $(foreach dir,$(SRCDIR_BASIC_generic),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s +build/target_mega65/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_mega65) $(foreach dir,$(SRCDIR_BASIC_mega65),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s +build/target_ultimate64/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_ultimate64) $(foreach dir,$(SRCDIR_BASIC_ultimate64),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s -build/basic_mega65/OUT.BIN: $(TOOLS_LIST) $(SRC_BASIC_mega65) build/basic/packed_messages.s -build/kernal_mega65/OUT.BIN: $(TOOLS_LIST) $(SRC_KERNAL_mega65) -build/newrom_mega65: build/basic_mega65/OUT.BIN build/kernal_mega65/OUT.BIN -build/newkern_mega65: build/newrom_mega65 Makefile -build/newbasic_mega65: build/newrom_mega65 Makefile +build/target_generic/OUTK.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_KERNAL_generic) $(foreach dir,$(SRCDIR_KERNAL_generic),$(wildcard $(dir)/*.s)) +build/target_mega65/OUTK.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_KERNAL_mega65) $(foreach dir,$(SRCDIR_KERNAL_mega65),$(wildcard $(dir)/*.s)) +build/target_ultimate64/OUTK.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_KERNAL_ultimate64) $(foreach dir,$(SRCDIR_KERNAL_ultimate64),$(wildcard $(dir)/*.s)) -build/basic_ultimate64/OUT.BIN: $(TOOLS_LIST) $(SRC_BASIC_ultimate64) build/basic/packed_messages.s -build/kernal_ultimate64/OUT.BIN: $(TOOLS_LIST) $(SRC_KERNAL_ultimate64) -build/newrom_ultimate64: build/basic_ultimate64/OUT.BIN build/kernal_ultimate64/OUT.BIN -build/newkern_ultimate64: build/newrom_ultimate64 Makefile -build/newbasic_ultimate64: build/newrom_ultimate64 Makefile +build/target_generic/newrom: build/target_generic/OUTB.BIN build/target_generic/OUTK.BIN +build/target_mega65/newrom: build/target_mega65/OUTB.BIN build/target_mega65/OUTK.BIN +build/target_ultimate64/newrom: build/target_ultimate64/OUTB.BIN build/target_ultimate64/OUTK.BIN +build/kernal_generic.rom: build/target_generic/newrom +build/kernal_mega65.rom: build/target_mega65/newrom +build/kernal_ultimate64.rom: build/target_ultimate64/newrom + +build/basic_generic.rom: build/target_generic/newrom +build/basic_mega65.rom: build/target_mega65/newrom +build/basic_ultimate64.rom: build/target_ultimate64/newrom # Rules - BASIC and KERNAL -build/basic/packed_messages.s: $(TOOL_COMPRESS_TEXT) - @mkdir -p build/basic - $(TOOL_COMPRESS_TEXT) > build/basic/packed_messages.s +build/,generated/gen_packed_messages.s: $(TOOL_COMPRESS_TEXT) + @mkdir -p build/,generated + $(TOOL_COMPRESS_TEXT) > build/,generated/gen_packed_messages.s -.PRECIOUS: build/basic_%/OUT.BIN -build/basic_%/OUT.BIN: $(TOOLS_LIST) $(SRC_BASIC_$*) build/basic/packed_messages.s - @mkdir -p build/basic_$* - @rm -f build/basic_$*/* - @for SRC in $(SRC_BASIC_$*); do \ - ln -s ../../$$SRC build/basic_$*/$$(basename $$SRC); \ - done - @ln -s ../../build/basic/packed_messages.s build/basic_$*/packed_messages.s - $(TOOL_PREPROCESS) -d build/basic_$* -l a000 -h e4d2 +.PRECIOUS: build/target_%/OUTB.BIN +build/target_%/OUTB.BIN: + @mkdir -p build/target_$* + @rm -f $@* build/target_$*/BASIC* + @$(TOOL_BUILD_SEGMENT) -s BASIC -i BASIC-$* -o OUTB.BIN -t build/target_$* -l a000 -h e4d2 $(SRCDIR_BASIC_$*) build/,generated -.PRECIOUS: build/kernal_%/OUT.BIN -build/kernal_%/OUT.BIN: $(TOOLS_LIST) $(SRC_KERNAL_$*) - @mkdir -p build/kernal_$* - @rm -f build/kernal_$*/* - @for SRC in $(SRC_KERNAL_$*); do \ - ln -s ../../$$SRC build/kernal_$*/$$(basename $$SRC); \ - done - $(TOOL_PREPROCESS) -d build/kernal_$* -l e4d3 -h ffff +.PRECIOUS: build/target_%/OUTK.BIN +build/target_%/OUTK.BIN: + @mkdir -p build/target_$* + @rm -f $@* build/target_$*/KERNAL* + @$(TOOL_BUILD_SEGMENT) -s KERNAL -i KERNAL-$* -o OUTK.BIN -t build/target_$* -l e4d3 -h ffff $(SRCDIR_KERNAL_$*) -.PRECIOUS: build/newrom_% -build/newrom_%: build/basic_%/OUT.BIN build/kernal_%/OUT.BIN - cat build/basic_$*/OUT.BIN build/kernal_$*/OUT.BIN > build/newrom_$* +.PRECIOUS: build/target_%/newrom +build/target_%/newrom: + cat build/target_$*/OUTB.BIN build/target_$*/OUTK.BIN > $@ -.PRECIOUS: build/newkern_% -build/newkern_%: build/newrom_% Makefile - dd if=build/newrom_$* bs=8192 count=1 skip=2 of=build/newkern_$* +.PRECIOUS: build/kernal_%.rom +build/kernal_%.rom: + dd if=build/target_$*/newrom bs=8192 count=1 skip=2 of=$@ -.PRECIOUS: build/newbasic_% -build/newbasic_%: build/newrom_% Makefile - dd if=build/newrom_$* bs=8192 count=1 skip=0 of=build/newbasic_$* +.PRECIOUS: build/basic_%.rom +build/basic_%.rom: + dd if=build/target_$*/newrom bs=8192 count=1 skip=0 of=$@ # Rules - platform 'Mega 65' specific -build/newc65: build/newkern_mega65 build/newbasic_mega65 build/chargen Makefile - dd if=/dev/zero bs=4096 count=10 of=build/newc65 - cat build/newbasic_mega65 build/chargen build/chargen build/newkern_mega65 >> build/newc65 - dd if=/dev/zero bs=65536 count=1 of=build/basic/padding - cat build/basic/padding >> build/newc65 +build/newc65.rom: build/kernal_mega65.rom build/basic_mega65.rom build/chargen.rom Makefile + dd if=/dev/zero bs=4096 count=10 of=build/newc65.rom + cat build/basic_mega65.rom build/chargen.rom build/chargen.rom build/kernal_mega65.rom >> build/newc65.rom + dd if=/dev/zero bs=65536 count=1 of=build/padding + cat build/padding >> build/newc65.rom + rm -f build/padding # Rules - tests -test: build/newkern_generic build/newbasic_generic - x64 -kernal build/newkern_generic -basic build/newbasic_generic -8 empty.d64 +test: build/kernal_generic.rom build/basic_generic.rom + x64 -kernal build/kernal_generic.rom -basic build/basic_generic.rom -8 empty.d64 -testremote: build/newkern_generic build/newbasic_generic - x64 -kernal build/newkern_generic -basic build/newbasic_generic -remotemonitor +testremote: build/kernal_generic.rom build/basic_generic.rom + x64 -kernal build/kernal_generic.rom -basic build/basic_generic.rom -remotemonitor testm65: build/newc65 - m65 -b ../mega65-core/bin/mega65r1.bit -k ../mega65-core/bin/KICKUP.M65 -R build/newc65 -4 + m65 -b ../mega65-core/bin/mega65r1.bit -k ../mega65-core/bin/KICKUP.M65 -R build/newc65.rom -4 -testsimilarity: build/newrom_generic $(TOOL_SIMILARITY) - $(TOOL_SIMILARITY) kernal build/newrom_generic - $(TOOL_SIMILARITY) basic build/newrom_generic +testsimilarity: build/target_generic/newrom $(TOOL_SIMILARITY) + $(TOOL_SIMILARITY) kernal build/target_generic/newrom + $(TOOL_SIMILARITY) basic build/target_generic/newrom diff --git a/bin/REEADME.txt b/bin/README.txt similarity index 100% rename from bin/REEADME.txt rename to bin/README.txt diff --git a/bin/basic_generic.rom b/bin/basic_generic.rom index 71a7c830c8541bd840c0533b5aff008e0eefb3c0..966d794a1402a3a82ca753a7d6d084d97a429ce9 100644 GIT binary patch delta 1902 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 diff --git a/bin/basic_ultimate64.rom b/bin/basic_ultimate64.rom index 0d1f0ceff0912949bc158a79909bb1cf52c28b74..482ff3978570e8dde9fcd0250a7babe2bb528d23 100644 GIT binary patch delta 1864 zcmYjSYfKbZ6rKm`EDSIU1-liM>#KkWq4?-Z)q)A>w!Jon*7T*xKx?y@*0f3cLuj(Z zI7&=v*N!G=Lo$qeXLlAgON}uiucRs6*|v6KNSoHA9UxG&iJgK~)adriE+!_sbMC$8 zb#HR1mbuj3gDpL>oWDP4 zRGR`3;AV_h1ZM(Xr+P)SUe!~Qpiq5nMz6`i`U6Cbr?)kspNzV=L7MGcN6lnWPyrqI zl4i5i5%Jxq@$PMrT2xwLFSfK&CtGGZ%{w6$nF<)eH+#U3>OR?=^gNJ0mUHsnVD=5k zVqWbF`-b9Iy&|cUwvu(yHg&5)qqB@bnXHqa<25YKRnV-8HVol0RbheW?}Se*01?W) zxhqO{p3%n4aw8a89rJhj^?7NdJ}GTFS`myXDYQ8%|DB8EU-{Fkz)wO%tff-u=T!WG zk!O3XogtUH7+)XirkEM*3d_6r)y!m6Zs(WQ2Zuh6J|hjMNTvtF#T$R)@ouoR_ooxeC(F(49%oTmb(V?(FQ$83~5| z3@BxA+hV_(Vl~f4P3|o4*Kp*uz%xD7?9Jyme{ylqrA>mBM=M>&8@em-I*W^#!i#MLo zoXrUh|DlE5mnk}1I7QLZZjeh$0pp-HLpX!Bvyvc_)F}{_2i!HQ4r*cE7M@hXpJ|RI zP#EQG7H`*NA-g!ZXTtQ1{t~ykWzKJ}>Bmhx<+P#V? zwd5`C<@NoM4{KOGrKA!TW#?a(CZHea=mv z02He&qmJ|(%sMn#|mlVOOuht&Ut9>M+Nj3X(ptPlX&8m za>KmFP4k2|uA3|#*9U_X{oDMcIW?0Q%{6B=J_$K_DmgemSs~cGLybpJgrmCBkzSj0 z0z#yKfE)lrvAj{E?Wk8pzi%h|UAi~#jR%P|u} zLN6Ljq2Nd=#9~*kqrWS54w|*beES^jkS2UT_9O}nx5pJ{ag`fXYs{$I$ z*iopFopuqrlrjYv6KT_sr3Bs|6T!Ypb=G7%J#sBIW8Mb)I!|f+iU@ax`^ZaNJJ!ys zBxHXJ*U8NI;&DH$18G}-hgKftw5^ok0V8HhQF426;H6VX@?dEd3y)oiCB?#$%7q(v$s?@7fl$>)Y2gBLm| z&O;hta6vR{EL=N|a1V*Q`s^YV!Q(*sU_y{Me9|}G3}`>_s&Iu&MG*@lqA3zClUVUQzxfryOSg WSNxLv&xhg7a*WIkI}@|aQ};j0p+3(5 delta 3178 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 Vn2fDokwj6=PVSuR%&mv zx0jPCgq_hTD4pj?4Jr#u=i6mQ;cz%CXD6o|B@06MQn-MRjmo=}CFtO&T&ZNYbqvW! zrH1Z#Nr=SjpsQ_b=!geh@rI6}plhh%R7e>gx!^;tA+T({psbK*D$80!%1^w*hWq|u z*dv`2Lcj%BfZz?CI;YNV2zCrLxQ2oq@dj5sc*sMgk z&(6PI=VV_$fj8D(w|k~vmTo8+%?=hU2P`o;Ps#Mv3I7C1{tG}R33C;T6yjemg?E>X zCSNblQOqN$P-K^qBZbOuP`weGd_XbxPvIHLv}9sZI#J0$%fs-78FF#Jpzlf`OYU)| zTWj$~@nq(b67jk>)U(=2UmpT0!PGn4b&;`f_@Pl>UCTuVm59jvMp~7)(~Q`Iqv9d+ zn;<-#Rbl;QD!f#?o4CGF^()`%p{cSZ0bM;PtaP*@{SuIR%A ziX`x#BjPK0i+&5WmSt&TE?fT%138#;iI$SR0zx=)Xa}ouU+iF!~+i=Vtryo-uV=Yya4LteSq{O?IT8xHGo|ICL>0p2%Oq z-i(NM@(Wh6-}soOCf1*J$;!!TO1dW#3}jZx_M|m8u}O(jIlOj^noN^TV8T)p^OOmd zKrOyH+e)1_&aSqdj(E)ebr3BAWbwb0LcIUQ6hi*BDOBpON})3UniMMcuTCQIR|Q4X z=PLz|<-av%^Y38GSmi#O&(ySowcGKQF*|;1?C=)eoL1{*PIam?J;%^NHh8L&SJKLX zW^Wu1+@v&{Ydnh_X>P{V>sjnbD zmy!C=rg5nk!*IaTOBF9x{;&)>Uk*N-@J%l#t-anpo*)#*(HI@?#UxQN`Q|9se1+vi zY%a1@EgMIZnXm^h9fuqhAdsh)&~qsrtWdS;EU->*OR9ytsrg>V-Q;dK4z(MMilxPi zrb#VupryK#gH9!WJEP>+x$1}lq5L8&^{Zd`-(=++WieV1Mt8<(?-~+h>8vn7J4LXe zj5pfStzdl+8mi)E?Cl*g%2jz{Z|8kE1a_l=3D}8uj*Gvaxu_PpYDT;f30mOMuFb5R zrk2U9x}a+Vg$rSY_R<`*Mhoz;hRXDS1r~)CzTMu|NbGHRBxY|*k_18w1gyt?@cDJp z=OyrYL1xtdfDiT3_g*C}P}@h{&ea3m)a^bVzor_EyavsSLxV?$bWbi^!ugiz1IjIw zA4lJ5$fJYC11-e{MuzgXeT&VVZ@~K-3_E+O=?_@Gf-Uw^!Aozh8m_&D5d#1lpcJjS2KlVtqAS2mG!o-pqY) zMT)V<&15JA&A)<8-(M17V%e7MD@D}rc@@*OYJ%F=& z9yy*qDbD9BpvocKY#J^)nZS=vLd`CU%$|C*7CJ6-Thms@T-;yg}DRo1uHTn0jL~_!rV#G{9 zF@;t9FHJTD;QGKn$!+R5%aZ(j1KbMuJiLZLDpgZC1-WHiN^>npjy> zoKZKizM6Hz`gIO(oPhbEiUfW+fj_>BKfQrx-Nm!-V$UR=tK%{qf3D$AHM~87KhyDk z9lt$^&uKWUVM)VlH2i>$n{~Wf7ki2e=L$OhlU`lQRnO@`Iw@-`!9Rp-t1Q4I+lEg$@V8dIU!HCamSc6}CJIrmeBVZ!8tk!Z1!tYQn zEJR~8Ko0f0kQnXC6DbXo%kZ_?O7cmo1zF312OsKp?w;XC;&zwGJUhOzb@zaw^_v3* zkY-$1TmbTGKz>d`UcV&7yY$m~hwNl&zihzPAJ%4ihw_vQl;?GZFV7uOu;u*gBZvhI zJQkGT7r%yG%jdD{Fx$%KtJa6tKr$cOTb*HgzGiO}h^^lXV%B^M1rD-nI^|~`7(stb zuzc}%rDfT}73r_0yR9<$9{)uq+nHeyToDoe+kYwfBKMcTOb>ne5=#E3j<4@1d}iC0 zC&~6rPd!`o^v2C41v?FkcRja6y|s8*rq=ZQilxi5R_~Bh3R~zO-PWl$I`KU}@1;G9~q5>Cwle zB}gkNACYt@OIo^2TCiMNA+40MrLV4%a#l%?ul}0!gp|8Rnx7|`RBNTo>`x0z(C;hF zzpq63DQF`;*KRp8{)3Hl@4OUTdIzsteg%2zN`1hxc>ozJePxK;D@a98-p3Q)4y?oH zit0-WOuJC=vK`2`=2`(-hu5OtEJ0Hr7JS=}HtbBjwBb24@b{NWQ2UwpmspM+NKe6I z<>=RA^&5AZK0?I}_m)_G^qe0xjPKo}AM+#W`PA%&VlQ4<(t|v29q_XmG9$@d|j}iL4!i|qTj>c18_MNkd#p&P$ghQ?mn ziVmP4O5I*G=3ivKkTf}oysKaQ2z~!%ddeb;DaCTG2m%3jOYf}Aga>ZuFD^%w*taVC zMg7aD_}FSX9F!bgasRP=^f;RMF$Pm7#_6!tGfwxv=-yHg?kxqEaMdy7Ws!Fj)t8td zo!&QUkuMoNIf0gpg|wOIJUZ-T;7=zYC(B zzQD@G$n)F9$d|2_l<>#c5qsQt*4U_Fa8_^zApxJgvt};0J1J*D&3R-^Ir?+TsyJFj zS>uoXj5=CN;qBvO(-5~wrC*p3tYInUu;HX3whUh!!WBXMN(dhaYRT?tj!sX8~r53BinKh$|Uz-7d@!8N??MH?Go<8L>wFh2iO~jO|obFDVI`Qj<@@q zN&F3bRx+pJJzlLGcP=vnk>0=z$luT~Ww_1D-)M9pm}=khxZF>M z%9SD%G`NMiw!X0Xjy{m#ds#QWIszPk*xc>C;{58)rk?8Bp3WwJb*;a%y;ry=>z4I& zxMfY(g&5GLBsrfNbc~Q-)aBRG-H=LPwe>aMLcsjiu)U)07Q*$M zE#_s#RgR9uly-z$75ge6jj?*&#fWt6V%MMzq&al>94F4BV!SL7M0e*I~*TAq{!FI#Cu&$$<&gHKO>5x5}yQzo0 zx}~DFr^#P)h;md>wSM5cMlO1R%3rm)YkM4r^1LDtU1nX9Qs*K#&1noSwCj>t3!qY58p89Ct-IAw;Oz z1BT0w&(YzdL1#hp9@yC z>PjfSrEU+!H`jfS>O6RozbMA=Ewa;a;lb|)!@l#)_?vR60zUlceXI_UL*alYSGfVFtp8vc_+TKvYdILA+B`y&%8`F?n6WYIBQ0?e$zL3wWRbz;(Vj(P)J zWn!kbddwq$=?}bohxjqn*`*eq!0&mX+VYg>VO{)3Y!~cF?k|b+=2nks7#OH_wcHW7 zOXecVF>W$3$Ha0~mk(Zn41mvki|$_#{>K5lH2~3t!|Dydl`k9?3?p>_!W)ojNpiUc z0(K^x@-U1A|MYu!3f?|nL$gS+m+|MZEBHr&>4kv+UMedy0kBpE{v6=VI=%Qe{99Q) z)bm<^uanEH#GeHDQ`69GA2eB=UCEweU?4-r-@NFMsLG2gC6zd=0TG`otQ6ngFAx*%TZn$HgwOwxVa zh15k`VJN&bGf4$MV~uK!s<^vb5>#FExI9k5L|iNwjH5qqHSXd5oxVC)SG2CZ&yCH| zpKF^Ht9Tl+asJ&Sl&HLVgu;^({CQDrLCq&9%uRH*pNAO(UsM?eM-8QmX*B9Kr0tt% zY~6QXoYm0rkDpPZ>h!CWr-QM??1Of1pWv>?=TSE&I67g$mVZvzQB4iyP~6ceT?#>E zjR6M8PdwayYN%;U3H`HvQf8UuiUJl&W{I6)kue~1uO#SXrS%E>O&2Guv5uYKEe42o zJR5;DjvWyQJGMt4tz%mR%61e-pj<~$1fnk3A+YprjuN%q$0;$h`%Owrx1DcOLY;3; z)SZA~dA^IjWd+959MwfbnOuq7;aO?(N+%7DBUCmeBpB>bHAg6;kvuzTB&Cy0ihT-| z2ENTkx%p}7D$6{pmf10NKxDmxWSlLDKCdMP9BgW8vcGz2vj;{aWSzEam=XO0wHWa;Hd2&~nF$ N7=(Tj+9PH9{te*+#;5=Q delta 3025 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 delta 3025 zcmY*b0Zn2fDokwj6=PVSuR%&mv zx0jPCgq_hTD4pj?4Jr#u=i6mQ;cz%CXD6o|B@06MQn-MRjmo=}CFtO&T&ZNYbqvW! zrH1Z#Nr=SjpsQ_b=!geh@rI6}plhh%R7e>gx!^;tA+T({psbK*D$80!%1^w*hWq|u z*dv`2Lcj%BfZz?CI;YNV2zCrLxQ2oq@dj5sc*sMgk z&(6PI=VV_$fj8D(w|k~vmTo8+%?=hU2P`o;Ps#Mv3I7C1{tG}R33C;T6yjemg?E>X zCSNblQOqN$P-K^qBZbOuP`weGd_XbxPvIHLv}9sZI#J0$%fs-78FF#Jpzlf`OYU)| zTWj$~@nq(b67jk>)U(=2UmpT0!PGn4b&;`f_@Pl>UCTuVm59jvMp~7)(~Q`Iqv9d+ zn;<-#Rbl;QD!f#?o4CGF^()`%p{cSZ0bM;PtaP*@{SuIR%A ziX`x#BjPK0i+&5WmSt&TE?fT%138#;iI$SR0zx=)Xa}ouU+iF!~+i=Vtryo-uV=Yya4LteSq{O?IT8xHGo|ICL>0p2%Oq z-i(NM@(Wh6-}soOCf1*J$;!!TO1dW#3}jZx_M|m8u}O(jIlOj^noN^TV8T)p^OOmd zKrOyH+e)1_&aSqdj(E)ebr3BAWbwb0LcIUQ6hi*BDOBpON})3UniMMcuTCQIR|Q4X z=PLz|<-av%^Y38GSmi#O&(ySowcGKQF*|;1?C=)eoL1{*PIam?J;%^NHh8L&SJKLX zW^Wu1+@v&{Ydnh_X>P{V>sjnbD zmy!C=rg5nk!*IaTOBF9x{;&)>Uk*N-@J%l#t-anpo*)#*(HI@?#UxQN`Q|9se1+vi zY%a1@EgMIZnXm^h9fuqhAdsh)&~qsrtWdS;EU->*OR9ytsrg>V-Q;dK4z(MMilxPi zrb#VupryK#gH9!WJEP>+x$1}lq5L8&^{Zd`-(=++WieV1Mt8<(?-~+h>8vn7J4LXe zj5pfStzdl+8mi)E?Cl*g%2jz{Z|8kE1a_l=3D}8uj*Gvaxu_PpYDT;f30mOMuFb5R zrk2U9x}a+Vg$rSY_R<`*Mhoz;hRXDS1r~)CzTMu|NbGHRBxY|*k_18w1gyt?@cDJp z=OyrYL1xtdfDiT3_g*C}P}@h{&ea3m)a^bVzor_EyavsSLxV?$bWbi^!ugiz1IjIw zA4lJ5$fJYC11-e{MuzgXeT&VVZ@~K-3_E+O=?_@Gf-Uw^!Aozh8m_&D5d#1lpcJjS2KlVtqAS2mG!o-pqY) zMT)V<&15JA&A)<8-(M17V%e7MD@D}rc@@*OYJ%F=& z9yy*qDbD9BpvocKY#J^)nZS=vLd`CU%$|C*7CJ6-Thms@T-;yg}DRo1uHTn0jL~_!rV#G{9 zF@;t9FHJTD;QGKn$!+R5%aZ(j1KbMuJiLZLDpgZC1-WHiN^>npjy> zoKZKizM6Hz`gIO(oPhbEiUfW+fj_>BKfQrx-Nm!-V$UR=tK%{qf3D$AHM~87KhyDk z9lt$^&uKWUVM)VlH2i>$n{~Wf7ki2e=L$OhlU`lQRnO +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(WIN32) || defined(_WIN32) + #define DIR_SEPARATOR "\\" +#else + #define DIR_SEPARATOR "/" +#endif + +const std::string ASM_CMD = "java -jar assembler/KickAss.jar "; +const std::string LAB_OUT_START = "__routine_START_"; +const std::string LAB_OUT_END = "__routine_END_"; +const std::string LAB_IN_START = ".label __routine_START_"; +const std::string LAB_IN_END = ".label __routine_END_"; + +const std::string BANNER_LINE = "//-------------------------------------------------------------------------------------------"; + +// +// Command line settings +// + +std::string CMD_outFile = "OUT.BIN"; +std::string CMD_tmpDir = "./out"; +std::string CMD_segName = "MAIN"; +std::string CMD_segInfo = "(unnamed)"; +int CMD_loAddress = 0xC000; +int CMD_hiAddress = 0xCFFF; + +std::list CMD_dirList; + +// +// Common helper functions +// + +void bailOut() +{ + exit(-1); +} + +void bailOut(const std::string &message) +{ + std::cout << std::endl << "FATAL: " << message << std::endl << std::endl; + exit(-1); +} + +void printUsage() +{ + std::cout << std::endl << + "usage: build_segment [-o ] [-d ] [-t ]" << std::endl << + " [-l ] [-h ]" << std::endl << + " [-s ] [-i ] " << + std::endl << std::endl; +} + +void printBannerLineTop() +{ + std::cout << std::endl << std::endl << std::endl << BANNER_LINE << std::endl; +} + +void printBannerLineBottom() +{ + std::cout << BANNER_LINE << std::endl << std::endl; +} + +void printBannerCollectAnalyse() +{ + printBannerLineTop(); + std::cout << "// Segment '" << CMD_segInfo << "' - collecting and analysing routines" << std::endl; + printBannerLineBottom(); +} + +void printBannerBinCompile() +{ + printBannerLineTop(); + std::cout << "// Segment '" << CMD_segInfo << "' - binning and compiling the assembly" << std::endl; + printBannerLineBottom(); +} + +// +// Class definitions +// + +class SourceFile +{ +public: + SourceFile(const std::string &fileName, const std::string &dirName); + + std::string fileName; + std::string dirName; + + bool floating; + std::vector content; + + std::string label; + int startAddr; // for fixed (non-floating) routines only + int codeLength; // for both fixed and floating routines + + int testAddrStart; // start address during test run + int testAddrEnd; // end address during test run +}; + +class BinningProblem +{ +public: + + BinningProblem() {}; + BinningProblem(int loAddress, int hiAddress); + + bool isSolved() const; + + void addToProblem(SourceFile *routine); + void fillGap(int gapAddress, const std::list &routines); + void performObviousSteps(); + void removeUselessGaps(); + void sortFloatingRoutinesBySize(); + + std::map fixedRoutines; // routines with location already fixed + std::map gaps; // gaps by address + std::vector floatingRoutines; // routines not allocated to any address yet; always keep them sorted! +}; + +class Solver +{ +public: + Solver(BinningProblem &problem) : problem(problem) {} + + void run(); + + int selectGapToFill(); + void findPartialSolution(int gapSize, std::list &partialSolution); + +private: + + BinningProblem &problem; +}; + +// +// Global variables +// + +std::list GLOBAL_sourceFiles; +std::list GLOBAL_sourceFiles_noCode; +size_t GLOBAL_maxFileNameLen = 0; +size_t GLOBAL_totalRoutinesSize = 0; +BinningProblem GLOBAL_binningProblem; + +// +// Top-level functions +// + +void parseCommandLine(int argc, char **argv) +{ + int opt; + + // Retrieve command line options + + while ((opt = getopt(argc, argv, "o:d:t:l:h:s:i:")) != -1) + { + switch(opt) + { + case 'o': CMD_outFile = optarg; break; + case 't': CMD_tmpDir = optarg; break; + case 's': CMD_segName = optarg; break; + case 'i': CMD_segInfo = optarg; break; + case 'l': CMD_loAddress = strtol(optarg, nullptr ,16); break; + case 'h': CMD_hiAddress = strtol(optarg, nullptr ,16); break; + default: printUsage(); bailOut(); + } + } + + // Retrieve directory list + + for (int idx = optind; idx < argc; idx++) + { + CMD_dirList.push_back(argv[idx]); + } + + if (CMD_dirList.empty()) { printUsage(); bailOut("empty directory list"); } +} + +void readSourceFiles() +{ + for (const auto &dirName : CMD_dirList) + { + DIR *dirHandle = opendir(dirName.c_str()); + if (!dirHandle) bailOut(std::string("unable to open directory '") + dirName + "'"); + + struct dirent *dirEntry; + while ((dirEntry = readdir(dirHandle)) != nullptr) + { + const std::string fileName = dirEntry->d_name; + + // Filter-out files which are not assembler files, temporary, etc. + + if (fileName.length() < 3) continue; + if (fileName.front() == '#') continue; + if (fileName.front() == '~') continue; + if (fileName.substr(fileName.length() - 2) != ".s") continue; + + // Add file to the global list + + GLOBAL_sourceFiles.push_back(SourceFile(fileName, dirName)); + GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, fileName.length()); + } + + closedir(dirHandle); + } + + if (GLOBAL_sourceFiles.empty()) bailOut("no source files found"); + + // Sort the file list by name, to provide deterministic results + + auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.fileName.compare(b.fileName) < 0; }; + GLOBAL_sourceFiles.sort(compare); +} + +void checkInputFileLabels() +{ + std::set usedLabels; + for (const auto &sourceFile : GLOBAL_sourceFiles) + { + if (usedLabels.count(sourceFile.label) != 0) + { + bailOut(std::string("input file '") + sourceFile.fileName + "' has a name too similar to another one"); + } + + usedLabels.insert(sourceFile.label); + } +} + +void calcRoutineSizes() +{ + const std::string nameBase = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_sizetest"; + + const std::string outFileName = nameBase + ".s"; + const std::string symFileName = nameBase + ".sym"; + + // Remove old files + + unlink(outFileName.c_str()); + unlink(symFileName.c_str()); + + // Write test file to determine routine sizes + + std::ofstream outFile(outFileName, std::fstream::out | std::fstream::trunc); + if (!outFile.good()) bailOut(std::string("can't open temporary file '") + outFileName + "'"); + + // Start at $100, so that no local data gets accesses using ZP addressing modes + // during this pass, which would otherwise upset things later + + outFile << std::endl << ".segment " << CMD_segName << " [start=$100, min=$100, max=$FFFF]" << std::endl; + + for (const auto &sourceFile : GLOBAL_sourceFiles) + { + outFile << std::endl << std::endl << std::endl << std::endl; + outFile << "// Source file: " << sourceFile.fileName << std::endl << std::endl; + outFile << LAB_OUT_START << sourceFile.label << ":" << std::endl << std::endl; + + outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); + + outFile << std::endl << std::endl; + outFile << LAB_OUT_END << sourceFile.label << ":" << std::endl; + } + + if (!outFile.good()) bailOut(std::string("error writing temporary file '") + outFileName + "'"); + outFile.close(); + + // All written - now launch the assembler + + const std::string cmd = ASM_CMD + outFileName + " -symbolfile -o /dev/null"; + std::cout << std::flush; + if (0 != system(cmd.c_str())) + { + bailOut("assembler running failed"); + } + + // Read addresses + + std::ifstream symFile; + symFile.open(symFileName); + if (!symFile.good()) bailOut(std::string("unable to open results file '") + symFileName + "'"); + + std::string line; + while (std::getline(symFile, line)) + { + bool startLabel; + + if (0 == line.compare(0, LAB_IN_START.size(), LAB_IN_START)) + { + line.erase(0, LAB_IN_START.length()); + startLabel = true; + } + else if (0 == line.compare(0, LAB_IN_END.size(), LAB_IN_END)) + { + line.erase(0, LAB_IN_END.length()); + startLabel = false; + } + else continue; + + // Decode address, write it into the object + + auto eqPos = line.rfind('='); + + auto address = strtol(line.substr(eqPos + 2).c_str(), nullptr ,16); + std::string refLabel = line.substr(0, eqPos); + + for (auto &sourceFile : GLOBAL_sourceFiles) + { + if (refLabel.compare(sourceFile.label) != 0) continue; + if (startLabel) + { + sourceFile.testAddrStart = address; + } + else + { + sourceFile.testAddrEnd = address; + } + } + } + + symFile.close(); + + // Calculate size of each and every routine + + for (auto &sourceFile : GLOBAL_sourceFiles) + { + if (sourceFile.testAddrStart <= 0 || sourceFile.testAddrEnd <= 0 || + sourceFile.testAddrStart > sourceFile.testAddrEnd) + { + bailOut(std::string("unable to determine code length in '") + sourceFile.fileName + "'"); + } + + sourceFile.codeLength = sourceFile.testAddrEnd - sourceFile.testAddrStart; + GLOBAL_totalRoutinesSize += sourceFile.codeLength; + } + + // Check whether total code size is sane + + if (0 == GLOBAL_totalRoutinesSize) bailOut("total code size is 0"); + if (signed(GLOBAL_totalRoutinesSize) > CMD_hiAddress - CMD_loAddress) + { + bailOut(std::string("total code size is ") + std::to_string(GLOBAL_totalRoutinesSize) + ", too much for this segment!"); + } + + // Sort the file list by code size, starting from the smallest + + auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.codeLength < b.codeLength; }; + GLOBAL_sourceFiles.sort(compare); + + // Move zero-sized element to a separate list + + while (GLOBAL_sourceFiles.front().codeLength == 0) + { + GLOBAL_sourceFiles_noCode.push_back(GLOBAL_sourceFiles.front()); + GLOBAL_sourceFiles.pop_front(); + } +} + +void prepareBinningProblem() +{ + // Prepare the log file + + const std::string logFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_binproblem.log"; + unlink(logFileName.c_str()); + std::ofstream logFile(logFileName, std::fstream::out | std::fstream::trunc); + std::string logLine; + + // Print out code length information + + for (const auto &sourceFile : GLOBAL_sourceFiles) + { + std::string spacing; + spacing.resize(GLOBAL_maxFileNameLen + 4 - sourceFile.fileName.length(), ' '); + std::string floating = sourceFile.floating ? "(floating) " : " "; + logLine = "file: " + floating + sourceFile.fileName + spacing + "size: " + std::to_string(sourceFile.codeLength); + + logFile << logLine << std::endl; + std::cout << logLine << std::endl; + } + + // Create the binning problem + + GLOBAL_binningProblem = BinningProblem(CMD_loAddress, CMD_hiAddress); + + for (auto &sourceFile : GLOBAL_sourceFiles) + { + GLOBAL_binningProblem.addToProblem(&sourceFile); + } + + // Print out some more statistics + + logLine = "free space (after floating routines are placed): " + + std::to_string(CMD_hiAddress - CMD_loAddress - GLOBAL_totalRoutinesSize) ; + logFile << std::endl << logLine << std::endl; + std::cout << std::endl << logLine << std::endl; + logLine = "number of floating routines: " + + std::to_string(GLOBAL_binningProblem.floatingRoutines.size()); + logFile << logLine << std::endl; + std::cout << logLine << std::endl; + logLine = "number of gaps for the floating routines: " + + std::to_string(GLOBAL_binningProblem.gaps.size()); + logFile << logLine << std::endl << std::endl; + std::cout << logLine << std::endl << std::endl; + + // Print out the available gaps + + logLine = "gap address: $"; + for (auto& gap : GLOBAL_binningProblem.gaps) + { + std::string gapSize = std::string(" size: ") + std::to_string(gap.second); + logFile << logLine << std::uppercase << std::hex << gap.first << gapSize << std::endl; + std::cout << logLine << std::uppercase << std::hex << gap.first << gapSize << std::endl; + } + + std::cout << std::endl; + + // Close the log file + + if (!logFile.good()) bailOut(std::string("error writing log file '") + logFileName + "'"); + logFile.close(); +} + +void solveBinningProblem() +{ + std::cout << "trying to solve the routine binning problem" << std::endl << std::endl; + + // Do some routine actions on the binning problem object + + Solver solver(GLOBAL_binningProblem); + solver.run(); + + if (!GLOBAL_binningProblem.isSolved()) + { + bailOut("unable to solve the routine binning problem"); + } + + std::cout << std::endl; +} + +void compileSegment() +{ + // First combine everything into one assembler file + + const std::string outFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_combined.s"; + unlink(outFileName.c_str()); + std::ofstream outFile(outFileName, std::fstream::out | std::fstream::trunc); + if (!outFile.good()) bailOut(std::string("can't open temporary file '") + outFileName + "'"); + + // Write the header + + outFile << std::endl << ".segment " << CMD_segName << + " [start=$" << std::hex << CMD_loAddress << + ", min=$" << std::hex << CMD_loAddress << + ", max=$" << std::hex << CMD_hiAddress << + ", outBin=\"" << CMD_outFile << "\", fill]" << + std::endl; + + // Write files which only contain definitions (no routines) + + for (const auto &sourceFile : GLOBAL_sourceFiles_noCode) + { + outFile << std::endl << std::endl << std::endl << std::endl; + outFile << "// Source file: " << sourceFile.fileName << std::endl << std::endl; + outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); + outFile << std::endl; + } + + // Write remaining files, these should be placed under their proper locations + + for (const auto &routine : GLOBAL_binningProblem.fixedRoutines) + { + outFile << std::endl << std::endl << std::endl << std::endl; + outFile << "// Source file: " << routine.second->fileName << std::endl << std::endl; + outFile << "\t* = $" << std::hex << routine.first << std::endl << std::endl; + outFile << std::string(routine.second->content.begin(), routine.second->content.end()); + outFile << std::endl; + } + + if (!outFile.good()) bailOut(std::string("error writing temporary file '") + outFileName + "'"); + outFile.close(); + + // All written - now launch the assembler + + const std::string cmd = ASM_CMD + outFileName + " -symbolfile -o " + CMD_outFile; + std::cout << std::flush; + if (0 != system(cmd.c_str())) + { + bailOut("assembler running failed"); + } +} + +// +// Main function +// + +int main(int argc, char **argv) +{ + parseCommandLine(argc, argv); + + printBannerCollectAnalyse(); + + readSourceFiles(); + checkInputFileLabels(); + calcRoutineSizes(); + + printBannerBinCompile(); + + prepareBinningProblem(); + solveBinningProblem(); + compileSegment(); + + return 0; +} + +// +// Class 'SourceFile' +// + +SourceFile::SourceFile(const std::string &fileName, const std::string &dirName) : + fileName(fileName), + dirName(dirName), + floating(false), + startAddr(-1), + codeLength(-1), + testAddrStart(-1), + testAddrEnd(-1) +{ + const std::string fileNameWithPath = dirName + DIR_SEPARATOR + fileName; + std::cout << "reading file: " << fileNameWithPath << std::endl; + + // Open the file + + std::ifstream inFile; + inFile.open(fileNameWithPath); + if (!inFile.good()) bailOut("unable to open file"); + + // Read the content + + inFile.seekg(0, inFile.end); + auto fileLength = inFile.tellg(); + if (fileLength == 0) bailOut("file is empty"); + content.resize(fileLength); + + inFile.seekg(0); + inFile.read(&content[0], fileLength); + if (!inFile.good()) bailOut("error reading file content"); + + inFile.close(); + + // Determine if the file content is floating or fixed position one, + // retrieve start address + + auto isHexDigit = [](char digit) -> bool + { + return (digit >= '0' && digit <= '9') || + (digit >= 'a' && digit <= 'f') || + (digit >= 'A' && digit <= 'F');; + }; + + floating = !(fileName.length() >= 6 && fileName[4] == '.' && + isHexDigit(fileName[0]) && + isHexDigit(fileName[1]) && + isHexDigit(fileName[2]) && + isHexDigit(fileName[3])); + + if (!floating) + { + startAddr = strtol(fileName.substr(0, 4).c_str(), nullptr ,16); + } + + // Generate KickAss compatible label from the file name + + label = fileName.substr(0, fileName.length() - 2); + + std::replace(label.begin(), label.end(), '.', '_'); + std::replace(label.begin(), label.end(), ',', '_'); +} + +// +// Class 'BinningProblem' +// + +BinningProblem::BinningProblem(int loAddress, int hiAddress) +{ + if (hiAddress < loAddress || hiAddress < 0 || loAddress < 0 || hiAddress > 0xFFFF || loAddress > 0xFFFF) + { + bailOut("invalid lo/hi address"); + } + + // Create one large gap (initial) to represent the assembly + + gaps[loAddress] = hiAddress - loAddress + 1; +} + +bool BinningProblem::isSolved() const +{ + return floatingRoutines.empty(); +} + +void BinningProblem::addToProblem(SourceFile *routine) +{ + if (routine->floating) + { + floatingRoutines.push_back(routine); + } + else + { + // For the fixed-address routines, we have to find the matching place + + bool gapFound = false; + for (auto &gap: gaps) + { + if (gap.first > routine->startAddr || gap.first + gap.second - 1 < routine->startAddr) + { + continue; // not a suitable gap + } + + // We have to put the routine into the current gap + + if (gap.first + gap.second < routine->startAddr + routine->codeLength) + { + bailOut(std::string("fixed address file '") + routine->fileName + "' won't fit in the available gap"); + } + + gapFound = true; + + // Put the routine into the gap, possibly removing it or splitting into two + + fixedRoutines[routine->startAddr] = routine; + + // Calculate possible new gap after the routine + + int newGapSize = (gap.first + gap.second) - (routine->startAddr + routine->codeLength); + int newGapStart = (newGapSize <= 0) ? -1 : routine->startAddr + routine->codeLength; + + // Remove or shrink the current gap + + if (gap.first == routine->startAddr) + { + gaps.erase(gap.first); + } + else + { + gap.second = routine->startAddr - gap.first; + } + + // Add a new gap + + if (newGapStart > 0) gaps[newGapStart] = newGapSize; + + break; // iterator is not valid enymore, we have to stop the llop + } + + if (!gapFound) + { + bailOut(std::string("start address of fixed address file '") + routine->fileName + "' already occupied"); + } + } +} + +void BinningProblem::fillGap(int gapAddress, const std::list &routines) +{ + int offset = 0; + std::string spacing; + + for (auto &routine : routines) + { + int targetAddr = gapAddress + offset; + + spacing.resize(GLOBAL_maxFileNameLen + 4 - routine->fileName.length(), ' '); + std::cout << " $" << std::hex << targetAddr << std::dec << ": " << + routine->fileName << spacing << "size: " << routine->codeLength << std::endl; + + fixedRoutines[targetAddr] = routine; + offset += routine->codeLength; + + if (offset > gaps[gapAddress]) bailOut(std::string("internal error line ") + std::to_string(__LINE__)); + + floatingRoutines.erase(std::remove(floatingRoutines.begin(), floatingRoutines.end(), routine), floatingRoutines.end()); + } + + // Get rid of the gap, it's useless now + + if (offset == gaps[gapAddress]) + { + std::cout << "filled to the last byte" << std::endl; + } + else if (!isSolved()) + { + std::cout << "filled in - dropped bytes: " << gaps[gapAddress] << std::endl; + } + else + { + std::cout << "out of routines" << std::endl; + } + + gaps.erase(gapAddress); +} + +void BinningProblem::performObviousSteps() +{ + // Get the size of the biggest routine; if there is just one gap which + // can handle it - put the roputine exactly there + + std::string spacing; + + bool repeat = true; + while (repeat && !floatingRoutines.empty() && !gaps.empty()) + { + repeat = false; + + int routineSize = floatingRoutines.back()->codeLength; + + int gapAddress; + int matchingGaps = 0; + for (auto &gap : gaps) + { + if (gap.second >= routineSize) + { + matchingGaps++; + gapAddress = gap.first; + } + } + + if (matchingGaps == 1) + { + // Routine can only be placed in this particular gap - do so + + gaps[gapAddress] -= routineSize; + int targetAddr = gapAddress + gaps[gapAddress]; + fixedRoutines[targetAddr] = floatingRoutines.back(); + + std::cout << "forced reducing gap $" << std::hex << gapAddress << std::dec << " to size " << + gaps[gapAddress] << std::endl; + + spacing.resize(GLOBAL_maxFileNameLen + 4 - floatingRoutines.back()->fileName.length(), ' '); + std::cout << " $" << std::hex << targetAddr << std::dec << ": " << + floatingRoutines.back()->fileName << spacing << "size: " << + floatingRoutines.back()->codeLength << std::endl; + + floatingRoutines.pop_back(); + if (gaps[gapAddress] == 0) gaps.erase(gapAddress); + + repeat = true; + } + } +} + +void BinningProblem::removeUselessGaps() +{ + // Get the size of the smallest floating routine, + // remove all the gaps which are smaller in size + + int minUsefulSize = floatingRoutines[0]->codeLength; + + bool repeat = true; + while (repeat) + { + repeat = false; + for (auto &gap : gaps) + { + if (gap.second < minUsefulSize) + { + std::cout << "dropping gap: $" << std::hex << gap.first << std::dec << " (size: " << gap.second << ")" << std::endl; + gaps.erase(gap.first); + repeat = true; + break; + } + } + } +} + +void BinningProblem::sortFloatingRoutinesBySize() +{ + // Sort the unallocated routines by size, starting from the smallest one + + auto compare = [](const SourceFile *a, const SourceFile *b) -> bool { return a->codeLength < b->codeLength; }; + std::sort (floatingRoutines.begin(), floatingRoutines.end(), compare); +} + +// +// Class 'Solver' +// + +void Solver::run() +{ + problem.sortFloatingRoutinesBySize(); // just to be extra sure + + while (!problem.gaps.empty() && !problem.floatingRoutines.empty()) + { + problem.performObviousSteps(); + problem.removeUselessGaps(); + + if (problem.gaps.empty() || problem.floatingRoutines.empty()) break; + + int gapAddr = selectGapToFill(); + + std::cout << "selected gap: " << "$" << std::hex << gapAddr << std::dec << " (size: " << problem.gaps[gapAddr] << ")" << std::endl; + + std::list partialSolution; + findPartialSolution(problem.gaps[gapAddr], partialSolution); + problem.fillGap(gapAddr, partialSolution); + } + + if (problem.isSolved()) + { + std::cout << std::endl << "all the routines sucessfully placed" << std::endl << std::endl; + } +} + +int Solver::selectGapToFill() +{ + // Find the smallest gap to fill-in + + int gapAddr = -1; + int gapSize = -1; + + for (auto &gap : problem.gaps) + { + if (gapSize < 0 || gap.second < gapSize) + { + gapAddr = gap.first; + gapSize = gap.second; + } + } + + return gapAddr; +} + +int KS(const std::vector &routines, + std::vector> &cacheV, // value cache + std::vector>> &cacheS, // partial solution cache + int n, int C, + std::list &solution) // sequence of decisions for each routine, has to be empty when calling +{ + // This routine solves a knapsack problem using a dynamic programming + + // XXX Possible efficiency improvement for the future: try to consider more than one gap at once, + // this will need a serious rework of the dynamic cache handling and will probably hurt the performance + + if (n == 0) + { + // No more routines to take the decision... + return 0; + } + + if (C == 0) + { + // We have no capacity left - decisions for all the routines left should be not to take them + solution.resize(n, false); + return 0; + } + + auto &cachedV = cacheV[n - 1][C - 1]; + auto &cachedS = cacheS[n - 1][C - 1]; + if (cachedV >= 0) + { + // We already hold the solution in out cache - return it + solution = cachedS; + return cachedV; + } + + auto &codeSize = routines[n - 1]->codeLength; + if (codeSize > C) + { + // We can't take this routine, it's too large + + cachedV = KS(routines, cacheV, cacheS, n - 1, C, solution); + solution.push_back(false); + cachedS = solution; + + return cachedV; + } + + // Note: it it seems equally good to take this routine, or not - take it! + // Evaluation starts from the largest routines, and we should prefer to leave + // a larger number of smaller routines - as this gives more possibilities + // while optimizing the usage of further gaps + + std::list solution1; + int val1 = KS(routines, cacheV, cacheS, n - 1, C - codeSize, solution1) + codeSize; // take + + if (val1 == C) + { + // By taking this routine we are filling all the space - contrary to the classic + // knapsack problem, this is an optimal solution for us, don't waste time calculating + // the others + + cachedV = val1; + solution = solution1; + solution.push_back(true); + cachedS = solution; + return cachedV; + } + + std::list solution2; + int val2 = KS(routines, cacheV, cacheS, n - 1, C, solution2); // don't take + + if (val1 >= val2) + { + // It's better to take this particular routine + + cachedV = val1; + solution = solution1; + solution.push_back(true); + } + else + { + // It's better not to take this particular routine + + cachedV = val2; + solution = solution2; + solution.push_back(false); + } + + cachedS = solution; + return cachedV; +}; + +void Solver::findPartialSolution(int gapSize, std::list &partialSolution) +{ + // First filter out available routines, take only the ones not larger than our gap + + std::vector routines; + routines.clear(); + for (auto &routine : problem.floatingRoutines) + { + if (routine->codeLength <= gapSize) + { + routines.push_back(routine); + } + else break; // 'floatingRoutines' should be kept sorted + } + + // Prepare value cache + + std::vector> cacheV; + cacheV.resize(routines.size()); + for (auto &cacheRow : cacheV) cacheRow.resize(gapSize, -1); + + // Prepare partial solution cache + std::vector>> cacheS; + cacheS.resize(routines.size()); + for (auto &cacheRow : cacheS) cacheRow.resize(gapSize); + + // Calculate solution + + std::list solution; + KS(routines, cacheV, cacheS, routines.size(), gapSize, solution); + + if (routines.size() != solution.size()) bailOut(std::string("internal error line ") + std::to_string(__LINE__)); + + // Return the solution upstream + + while (!routines.empty()) + { + if (solution.back() != false) + { + partialSolution.push_front(routines.back()); + } + + routines.pop_back(); + solution.pop_back(); + } + + return; +} diff --git a/src/compress_text.c b/src/compress_text.c index ff0cc4eb..cf7e4751 100644 --- a/src/compress_text.c +++ b/src/compress_text.c @@ -479,7 +479,7 @@ int main(void) } else printf("\t.byte $%02x\n",packed_keywords[i]); } - printf("\t.const packed_keyword_table_len = $%X\n",packedkey_len); + printf("\t.label packed_keyword_table_len = $%X\n",packedkey_len); fprintf(stderr,"Error list takes %d bytes raw.\n",raw_size); fprintf(stderr,"%d token bytes used to encode all messages.\n",token_count); diff --git a/src/basic005.c b/src/outdated/basic005.c similarity index 100% rename from src/basic005.c rename to src/outdated/basic005.c diff --git a/src/prepare_startup.c b/src/outdated/prepare_startup.c similarity index 100% rename from src/prepare_startup.c rename to src/outdated/prepare_startup.c diff --git a/src/preprocess.c b/src/outdated/preprocess.c similarity index 100% rename from src/preprocess.c rename to src/outdated/preprocess.c