10 REM > SMJoin
   20 REM Build Sideways Modules into a single image
   30 :
   40 DIM ctrl% 31,mem% &4100:X%=ctrl%:Y%=X%DIV256
   50 ON ERROR REPORT:PROCClose_All:PRINT " at line ";ERL:END
   60 ptr%=0:in%=0
   70 PRINT "Enter input files, end with RETURN"
   80 REPEAT:INPUT LINE ": "in$:IF in$<>"":PROCadd
   90 UNTIL in$=""
  100 INPUT LINE "Enter output filename: "out$
  110 OSCLI "Save "+out$+" "+STR$~mem%+"+"+STR$~ptr%+" 0 FFFBBC00"
  120 ON ERROR END
  130 OSCLI "Stamp "+out$
  140 END
  150 :
  160 DEFPROCadd
  170 IF LEFT$(in$,1)="|":ENDPROC
  180 IF LEFT$(in$,1)="*":OSCLI MID$(in$,2):ENDPROC
  190 in%=OPENIN(in$):IF in%=0:PRINT"File '"in$"' not found":ENDPROC
  200 len%=EXT#in%:IF len%>&4100-ptr%:PRINT"File '"in$"' too long":CLOSE#in%:in%=0:ENDPROC
  210 IF ptr%:ptr%=ptr%+2
  220 PROCgbpb(4,in%,mem%+ptr%,len%,0):CLOSE#in%:in%=0
  230 reloc%=0:IF mem%?ptr%=0:IF (mem%?(ptr%+2) AND &80)=&80:reloc%=ptr%+(mem%!(ptr%+1) AND &3FFF)
  240 IF reloc%=0:IF ptr%:PRINT"File '"in$"' not relocatable, must be first entry":ENDPROC
  250 old%=ptr%:ptr%=FNlink:IF old%=ptr%:PRINT"File '"in$"' too long":ENDPROC
  260 FOR A%=ptr% TO &3FFF STEP 4:mem%!A%=0:NEXT A%
  270 IF old%<>0 OR ptr%<&3FFF:ENDPROC
  280 REPEAT:ptr%=ptr%-1:UNTIL mem%?ptr%<>mem%?&3FFF:ptr%=ptr%+1
  290 ENDPROC
  300 :
  310 DEFPROCbyte
  320 IF count%=0:byte%=mem%?reloc%:reloc%=reloc%+1:count%=8
  330 IF byte%AND1:addr%=(mem%!(here%-1)AND&3FFF)+code%:mem%?(here%-1)=addr%:mem%?here%=(addr%DIV256)+&80
  340 REM IF byte%AND1:PRINT" At ";~here%-code%+&8000;" (";~here%+&8000;") addr=";~addr%-code%+&8000;" reloc to ";~addr%+&8000
  350 byte%=byte%DIV2:count%=count%-1
  360 ENDPROC
  370 :
  380 DEFFNlink
  390 IF ptr%=0:IF reloc%=0:=len%   :REM First item, no relocation table
  400 IF ptr%=0:IF reloc%>0:=reloc% :REM First item, strip relocation table
  410 :
  420 REM Relocate loaded module
  430 code% =ptr%                   :REM Start of loaded code
  440 end%  =reloc%                 :REM End of loaded code
  450 count%=0                      :REM No bits read yet
  460 byte% =0                      :REM Relocation bitmap
  470 IF end%>&4000:=ptr%           :REM Even after removing reloc table, will be too long
  480 FOR here%=code% TO end%-1
  490     IF (mem%?here% AND &C0)=&80:PROCbyte
  500 NEXT here%
  510 :
  520 REM Look for last unlinked module
  530 REM Look for:
  540 REM    JSR xxxx:JMP yyyy
  550 REM or JSR xxxx:LDX &F4:JMP yyyy
  560 link%=-2
  570 REPEAT
  580     found%=FALSE
  590     IF mem%?link%=&20 AND (mem%!(link%+3) AND &FFFFFF)=&4CF4A6:found%=TRUE:next%=link%+6
  600     IF (mem%!link% AND &FF0000FF)=&4C000020:found%=TRUE:next%=link%+4
  610     IF link%<0:found%=TRUE:next%=link%+6
  620     IF found%:last%=link%:link%=mem%!next% AND &3FFF
  630 UNTIL found%=0
  640 REM last%=> JSR xxxx opcode field of last module
  650 REM link%=> Start of service handler of last module
  660 REM next%=> JMP yyyy address field of last module
  670 :
  680 REM Link new module to previous service handler
  690 mem%?(ptr%-2)=&20
  700 mem%?(ptr%-1)=link%
  710 mem%?(ptr%-0)=(link% DIV 256) OR &80
  720 mem%?(ptr%+1)=&A6
  730 mem%?(ptr%+2)=&F4
  740 :
  750 REM Link previous module to new module
  760 mem%?(next%+0)=ptr%-2
  770 mem%?(next%+1)=((ptr%-2) DIV 256) OR &80
  780 =end%
  790 :
  800 DEFPROCClose_All
  810 in%=in%:IFin%:A%=in%:in%=0:CLOSE#A%
  820 ENDPROC
  830 :
  840 DEFPROCgbpb(A%,chn%,addr%,num%,ptr%)
  850 ?X%=chn%:X%!1=addr%:X%!5=num%:X%!9=ptr%:IFPAGE<&FFFFF:CALL&FFD1:ENDPROC
  860 IFA%=1ORA%=3:PTR#?X%=X%!9
  870 REPEAT:IFA%=1ORA%=2:BPUT#?X%,?X%!1 ELSE IFA%=3ORA%=4:?X%!1=BGET#?X%
  880 X%!1=X%!1+1:X%!5=X%!5-1:UNTIL(EOF#?X%ANDA%>2)ORX%!5<1:ENDPROC